Swiftのクロージャはクロージャ自体が定義されている周辺のソースコードから
定数や変数の値をキャプチャして、クロージャのボディ内で
キャプチャした値を参照したり、変更したりすることができます。
今回はSwiftのクロージャのキャプチャリングに関する実験的なプログラムを
日々大量のバグを生み出し続けるWebプログラマー「エイチ」と
バグが大好物なエイチのペット「バグバグ」とともに実装し把握してみます。
バグバグ 「・・・」
エイチ 「バグバグ ねぇ バグバグ 起きなよ
Swiftのクロージャにキャプチャリングってのがあるぞ
ちょっと試してみよっと」
バグバグ 「!!」
エイチ 「Int型の変数resultの値をInt型の変数amountの値づつ
増加させてくれるクロージャを定義して
定数incrementer1に代入するよ」
var result = 0
var amount = 1
let incrementer1 = { () -> Int in result += amount; return result }
print(incrementer1())
print(incrementer1())
print(incrementer1())
エイチ 「よしっ 実行してみよう」
1
2
3
エイチ 「クロージャのボディ内で使われているresultやamountは
グローバルスコープで定義されている変数の値を
キャプチャしてるってことだね」
エイチ 「今度はネストされた関数でのキャプチャリングを見てみよう
関数makeIncrementer1()を作ってみるよ」
func makeIncrementer1(_ amount: Int) -> () -> Int {
var result = 100
func nestedIncrementer1() -> Int {
result += amount
return result
}
return nestedIncrementer1
}
let incrementer2 = makeIncrementer1(10)
print(incrementer2())
print(incrementer2())
print(incrementer2())
エイチ 「実行結果はっと」
110
120
130
エイチ 「関数nestedIncrementer1()がキャプチャしてるのは
グローバルスコープで定義されたresultとamountじゃないぞ
関数makeIncrementer1()をコールする際に引数に指定された値を
パラメータamountをキャプチャしてるんだね
あと、関数makeIncrementer1()内で定義した
ローカル変数resultをキャプチャしてる」
エイチ 「最後にネストされた関数が
グローバルスコープからキャプチできるか試すよ」
func makeIncrementer2() -> () -> Int {
func nestedIncrementer2() -> Int {
result += amount
return result
}
return nestedIncrementer2
}
let incrementer3 = makeIncrementer2()
print(incrementer3())
print(incrementer3())
print(incrementer3())
エイチ 「実行結果はっと」
4
5
6
エイチ 「ネストされた関数から
グローバルスコープ内でもキャプチャできるんだね」
エイチ 「クロージャ内で気軽に周辺の定数や変数を参照できるのは楽だね
気をつけないと循環参照になってメモリリークを起こすかもっ」
エイチ 「さあ バグバグ ソースコード 全部お食べ」
バグバグ 「バグバグ バグバグ バグバグ」
エイチ 「美味しかったろう バグバグ」