クロージャは、定義されているコンテキストにある定数や変数をキャプチャすることができます。クロージャは、定数や変数が定義されていた元のスコープにもはや存在しなくなっている場合でも、それらの定数や変数の値をクロージャの本体内から参照あるいは変更することができます。

Swift では、値をキャプチャできるクロージャの最もシンプルな形式は、別の関数の本体内に記述されるネストされた関数です。ネストされた関数は、外側の関数の引数をキャプチャすることができ、外側の関数内に定義されている定数や変数をキャプチャすることも可能です。

次はネストされた関数 incrementer を含む、関数 makeIncrementer の例です。ネストされた関数 incrementer() は、runningTotal と amount の値をコンテキストからキャプチャします。これらの値をキャプチャした後、呼び出されるたびに runningTotal を amount 増加させるクロージャとして、incrementer が makeIncrementer から返されます。

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

makeIncrementer の戻り値の型は () -> Int です。つまり、シンプルな値ではなく、関数を返します。返されるこの関数にはパラメータが無く、呼び出されるたびに Int 値を返します。関数が別の関数を返せることについては、Function Types as Return Types を確認してください。

関数 makeIncrementer(forIncrement:) は、incrementer が返す現在の累計数を保持する整数の変数 runningTotal を定義しています。この変数は 0 で初期化されています。

関数 makeIncrementer(forIncrement:) には外部名が forIncrement で、ローカル名が amount の Int パラメータが 1 つあります。このパラメータに渡される引数の値には、返される関数の incrementer が呼び出されるたびに runningTotal を増加させる数を指定します。関数 makeIncrementer は、実際の増加処理を実行するネストされた関数 incrementer を定義しています。この関数は、単に runningTotal に amount を追加し、結果を返します。

切り離して考えると、ネストされた関数 incrementer() は通常とは異なります。

func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}

関数 incrementer() にはパラメータが無いにもかかわらず、その関数の本体から runningTotal と amount を参照しています。関数の周辺から runningTotal と amountの参照をキャプチャし、その関数の本体内で使用します。参照をキャプチャすることで、makeIncrementer の呼び出しが終了したときに runningTotal と amount が消失せず、次に関数 incrementer が呼び出されるときに runningTotal を利用できるようにもしています。

NOTE

最適化として、値がクロージャによって変更されない、かつクロージャが生成された後に値が変更されない場合には、Swift は値のコピーを代わりにキャプチャして保持することがあります。

また Swift は、変数が必要なくなったときの変数の破棄に関わるすべてのメモリ管理についても処理します。

次は makeIncrementer の実行例です。

let incrementByTen = makeIncrementer(forIncrement: 10)

この例では、呼び出されるたびに変数 runningTotal に 10 を加える関数 incrementerを、定数 incrementByTen が参照するよう設定しています。

incrementByTen()
// 値 10 を返す
incrementByTen()
// 値 20 を返す
incrementByTen()
// 値 30 を返す

もう一つの incrementer を生成した場合、別の新しい変数 runningTotal の参照を独立して保持します。

let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// 値 7 を返す

元の incrementer (incrementByTen) を再び呼び出すと、独自の変数 runningTotal を加算しますが、incrementBySeven でキャプチャされた変数には影響しません。

incrementByTen()
// 値 40 を返す
NOTE
クラスインスタンスのプロパティにクロージャを代入して、そのインスタンスやメンバーを参照することでそのインスタンスをクロージャがキャプチャする場合、クロージャとインスタンス間に強い参照の循環を生成します。強い参照の循環を絶つために、Swift はキャプチャリストを使用します。詳しい情報は、Strong Reference Cycles for Closures を参照してください。

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.