Objective-C
Swift

Objective-CのBlocksとSwiftのクロージャでは変数のキャプチャが違う

More than 3 years have passed since last update.

まず、Swiftで以下のコードを実行してみる。


var sampleStr = "ABCD"

let showCaptureString = {(number:Int) -> String in
sampleStr + String(number)
}

showCaptureString(99)
sampleStr = "DCBA"
showCaptureString(99)

まず、showCaptureStringというクロージャを定義。

これは受け取った数字とキャプチャしたsampleStrを連結して返すもの。

1行だけなのでreturnは省略できる。

実行結果は、最初のshowCaptureString(99)ではABCD99が返る。

そして、sampleStrをDCBAに書き換えた後、もう一度同じようにshowCaptureString(99)を実行すると、DCBA99が返る。

これと同じような事をObjective-CのBlocks構文を用いて実装してみる。

    NSString *sampleStr = @"ABCD";

NSString *(^showCaptureString)(int) = ^(int number){
return [NSString stringWithFormat:@"%@%d",sampleStr,number];
};

NSLog(showCaptureString(99));
sampleStr = @"DCBA";
NSLog(showCaptureString(99));

最初のshowCaptureString(99)はABCD99が返る、これは自然。

しかし、sampleStrをDCBAに書き換えた後、showCaptureString(99)を実行しても同じようにABCD99という値が返る。

このような結果になる理由は、Objective-CのBlocksとSwiftのクロージャでは変数のキャプチャが異なるから。

Objective-Cでは一度キャプチャした値は後から変更されず、実行時にはキャプチャした時の値が使われる。

一方、Swiftではキャプチャした変数は実行時のものが使われる。

Objective-CでSwiftのクロージャのようにBlock内でキャプチャ変数を書き換えるには、__blockをつければ良い。

    __block NSString *sampleStr = @"ABCD";

NSString *(^showCaptureString)(int) = ^(int number){
return [NSString stringWithFormat:@"%@%d",sampleStr,number];
};

NSLog(showCaptureString(99)); //ABCD99
sampleStr = @"DCBA";
NSLog(showCaptureString(99)); //DCBA99