クロージャが引数として関数に渡され、関数がリターンした後にそのクロージャが呼び出される場合、クロージャは関数をエスケープすると言います。パラメータにクロージャを受け取る関数を宣言するとき、クロージャがエスケープしないことを示すために、パラメータ名の前に @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.