はじめに
パート1の続きです。
ネスト関数とクロージャ
前回の例題をネスト関数で置き換えました。結果は全く同じです。
func maker(_ a: Int, _ b: Int) -> (() -> Int) {
var localvar = 0
func localfunc() -> Int {
globalCount += 1 // globalCountは参照されるだけ
localvar += b // localvar, bがキャプチャされる
return localvar
}
return localfunc
}
メソッドとイニシャライザ
通常の関数と同様に、クラスなどのメソッド、イニシャライザもクロージャとして扱うことかできます。
class Friend {
let name: String
init(name: String) {
self.name = name
}
deinit {
print("deinit", name)
}
func sayName() {
print("私は\(name)です。")
}
func sayHello(to f: Friend) {
print("こんにちは、\(f.name)さん\(name)です。")
}
}
次のように実行してみます。
let clo1: (String) -> Friend = Friend.init
var clo2: (Friend) -> ()
do {
let fr1 = clo1("花子")
clo2 = fr1.sayHello(to:)
fr1.sayName()
}
let fr2 = Friend(name: "太郎")
clo2(fr2)
clo2 = fr2.sayHello
}
出力結果は以下のようになります。
私は花子です。
こんにちは、太郎さん花子です。
deinit 花子
まず、イニシャライザを定数clo1に代入し、これを使ってdo文のなかでインスタンスが生成できていることがわかります。次に変数clo2にクラスのインスタンスメソッドを代入していますが、do文から出ることによって定数fr1自体は消滅します。その後、clo2に引数を与えるとちゃんと実行できていることからdo文のなかで生成したインスタンスはまだ生存していることがわかります。変数clo2に別の値を代入するとこのインスタンスは解放されます。
キャプチャリスト
ここまで、クロージャはその周辺のローカル変数をキャプチャして共有できることを説明してきました。一方、クロージャが元の変数と必ずしも値を共有しなくてもよい場合もあります。そのような場合、クロージャのインスタンスを生成する際に変数のコピーを作り、クロージャないでそのコピーの値を使うことができます。
var a, b, c: () -> ()
do {
var count = 0
var name = "Objective-C"
a = { [] in print("A: \(count), \(name)") }
b = { [count] in print("B: \(count), \(name)") }
c = { [name, count] in print("C: \(count), \(name)") }
count = 1
name = "Swift"
}
a()
b()
c()
先頭に変数名を[]で囲んだものがつけられています。これをキャプチャリストといいます。この中に書き並べられた変数はクロージャの生成時にコピーが作成され、そのあとは元の変数の値に変更があっても影響されません。
出力結果は以下のようになります。
A: 1, Swift
B: 0, Swift
C: 0, Objective-C
おわりに
クロージャのキャプチャの説明は以上です。