0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Swift: defer と @escaping の違いを理解する

Posted at

Swift: defer@escaping の違いを理解する

非同期処理やリソースの解放を扱う際に、Swiftでは defer@escaping クロージャの両方を利用することがあります。これらは同じような場面で使われることもありますが、その動作や用途は大きく異なります。本記事では、両者の違いを実際のコードサンプルを通して解説します。


1. defer の動作を理解する

defer は、スコープの終了時に必ず実行されるコードを登録するための機能です。ファイル操作やリソース解放など、クリーンアップ処理を簡潔に記述できます。

以下は、非同期処理で defer を使用してファイルをクローズする例です。

import Foundation

func asyncFileOperationWithDefer() {
    let fileURL = URL(fileURLWithPath: "/path/to/file.txt")

    DispatchQueue.global().async {
        print("Start of async task")

        guard let fileHandle = try? FileHandle(forWritingTo: fileURL) else {
            print("FileHandle failed")
            return
        }

        defer {
            print("FileHandle closed")
            fileHandle.closeFile()
        }

        print("Writing to file...")
        fileHandle.write("Hello, Swift!".data(using: .utf8)!)

        print("End of async task")
    }
}

asyncFileOperationWithDefer()

実行結果(想定)

Start of async task
Writing to file...
End of async task
FileHandle closed
  • defer は非同期タスク内のスコープ終了時に実行されます。
  • この例では、ファイルのクローズ処理が defer によって保証されています。

2. @escaping クロージャの動作を理解する

一方で、@escaping クロージャは、非同期処理が完了したタイミングで任意の処理を実行する際に使います。特定のタスクが終了した後に通知を受けたり、別の処理を開始したりするのに便利です。

以下は、@escaping クロージャを使用して非同期処理の完了を通知する例です。

import Foundation

func asyncFileOperationWithEscaping(completion: @escaping (Result<Void, Error>) -> Void) {
    let fileURL = URL(fileURLWithPath: "/path/to/file.txt")

    DispatchQueue.global().async {
        print("Start of async task")

        guard let fileHandle = try? FileHandle(forWritingTo: fileURL) else {
            print("FileHandle failed")
            completion(.failure(NSError(domain: "FileError", code: 1, userInfo: nil)))
            return
        }

        print("Writing to file...")
        fileHandle.write("Hello, Swift!".data(using: .utf8)!)
        fileHandle.closeFile()

        print("End of async task")
        completion(.success(()))
    }
}

asyncFileOperationWithEscaping { result in
    switch result {
    case .success:
        print("File operation completed successfully.")
    case .failure(let error):
        print("File operation failed with error: \(error)")
    }
}

実行結果(想定)

Start of async task
Writing to file...
End of async task
File operation completed successfully.
  • 非同期処理が完了したタイミングで completion クロージャが呼び出されます。
  • ファイルのクローズ処理も含めて、全体の成功/失敗を通知することができます。

3. defer@escaping の違い

特性 defer @escaping
実行タイミング スコープ終了時 非同期処理の完了時
用途 クリーンアップ処理(リソース解放など) 非同期処理の結果通知や次の処理のトリガー
非同期処理の外で利用可能か ×
複数の呼び出し 一度のみ(スコープ終了時) 任意のタイミングで複数回呼び出し可能

4. どちらを選ぶべきか?

  • defer を使うべき場面:

    • 非同期処理の内部で、リソースの解放やエラーハンドリングをスコープ内に簡潔にまとめたい場合。
  • @escaping を使うべき場面:

    • 非同期処理の終了後に外部のコンポーネントに通知したい場合や、完了後の処理を柔軟に扱いたい場合。

5. 両者を組み合わせる

以下は、defer@escaping を組み合わせて、非同期タスク内でリソースを解放しつつ完了通知を行う例です。

import Foundation

func asyncFileOperationCombined(completion: @escaping (Result<Void, Error>) -> Void) {
    let fileURL = URL(fileURLWithPath: "/path/to/file.txt")

    DispatchQueue.global().async {
        print("Start of async task")

        guard let fileHandle = try? FileHandle(forWritingTo: fileURL) else {
            print("FileHandle failed")
            completion(.failure(NSError(domain: "FileError", code: 1, userInfo: nil)))
            return
        }

        defer {
            print("FileHandle closed")
            fileHandle.closeFile()
        }

        print("Writing to file...")
        fileHandle.write("Hello, Swift!".data(using: .utf8)!)

        print("End of async task")
        completion(.success(()))
    }
}

asyncFileOperationCombined { result in
    switch result {
    case .success:
        print("File operation completed successfully.")
    case .failure(let error):
        print("File operation failed with error: \(error)")
    }
}

この実装では、defer でリソースを解放しつつ、非同期タスクの完了を @escaping クロージャで通知しています。


まとめ

  • defer はスコープ内でのリソース管理やクリーンアップ処理に最適です。
  • @escaping クロージャは非同期処理の終了後に外部へ通知する際に使います。
  • 両者を適切に組み合わせることで、非同期処理を効率的かつ安全に実装することが可能です。

ぜひ実際のプロジェクトで試してみてください!

0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?