クロージャが引数として関数に渡され、関数がリターンした後にそのクロージャが呼び出される場合、クロージャは関数をエスケープすると言います。パラメータにクロージャを受け取る関数を宣言するとき、クロージャがエスケープしないことを示すために、パラメータ名の前に @noescape
と記述することができます。クロージャに @noescape
を付けることでクロージャの存続期間が明確になるため、コンパイラがより積極的に最適化できるようになります。
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
closure()
}
例として、sort(_:)
メソッドは要素を比較するための引数として、クロージャを受け取ります。ソート処理が完了した後は必要なくなるため、パラメータに @noescape
を付けています。
クロージャがエスケープできる方法の 1 つは、関数の外に定義されている変数にクロージャを保存することです。例えば、非同期に処理を開始する多くの関数が、完了時のハンドラーとしてクロージャを受け取ることとします。処理開始後に関数はリターンしますが、関数の処理を完了してもクロージャは呼び出されず、しばらく後に呼び出されることができるように、クロージャはエスケープする必要があります。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
completionHandlers.append(completionHandler)
}
関数 someFunctionWithEscapingClosure(_:)
が引数としてクロージャを受け取り、関数の外側で宣言された配列にそのクロージャを追加しています。この関数のパラメータに @noescape
を付けようとすると、コンパイルエラーになります。
@noescape
をクロージャに付けることで、クロージャ内では暗黙的に self
を参照するようになります。
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNoescapeClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// "200" と出力
completionHandlers.first?()
print(instance.x)
// "100" と出力
Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.