項目種別
補足
登場した回
伝えた回
分類
並行処理 (Concurrency)@escaping@nonescapingクロージャー (Closure)エスケーピング・クロージャー (Escaping Closure)ノンエスケーピング・クロージャー (Non-escaping Closure)クロージングオーバー (Closing Over)
記載日
Sep 28, 2022 6:58 AM
関連資料
Swift Concurrency の withCheckedContinuation に @escaping を付与していないクロージャーをクロージングオーバーできることが不思議に思えましたけれど、調べてみるとそこから Swift Concurrency の特徴が窺えてきました。
通常の Dispatch のような非同期処理の場合、クロージングオーバーされたクロージャーはスコープを超えて延命されるため @escaping を付与して延命可能にする必要がありますが、Swift Concurrency の場合は await で処理が中断されるため、延命しなくても通常の生存期間を超えて存在することはなくなり、そのため @escaping ではなくてもエラーにならない様子でした。
エラーになる場面
例えば次のコードだと、引数として用意されたf を、いつ呼ばれるかはスレッド次第な async メソッドに渡すことですなわち f が実行されるより前に関数を抜けることになるため、その生存範囲を超えて生存することになるため f に @escaping がないとエラーになります。
func a(f: () -> Void) async {
DispatchQueue.global().async {
f()
}
}エラーにならない場面
対して Swift Concurrency の場合、引数として用意された f がありますが、これを await を付与した関数にクロージングオーバーして渡したとしても、並行処理な await が付与された行はその処理が終わるまでここで中断されるため、元の関数 a の終了まで辿り着くことはありません。そうすると f は通常の生存期間内にあるため、それに @escaping がついている必要はない様子です。
func a(f: () -> Void) async {
await myWithCheckedContinuation { continuation in
f()
continuation.resume()
}
}