Swift 的 closure 即使和 Objective-C 的 block 非常的相像,但是在取得外部變數的值時,做法是不同的。
先看這兩段 code ,做的事情是一樣的:
- 宣告一個整數和一個字串
- 宣告一個 block
- 執行這個 block
- 變動整數和字串的值
- 再執行這個 block
Objective-C
NSInteger myInt = 1;
NSMutableString *myString = [@"Hello" mutableCopy];
void (^myBlock)(void) = ^() {
NSLog(@"%@, %@", @(myInt), myString);
};
myBlock();
myInt = 2;
[myString appendString:@", world"];
myBlock();
Swift
var myInt = 1
var myString = "Hello"
let myBlock = {
print("\(myInt), \(myString)")
}
myBlock()
myInt = 2
myString.appendContentsOf(", world!")
myBlock()
印出結果
可以發現兩個語言在類似語句,呈現結果卻不一樣的結果:
Objective-C
1, Hello
1, Hello, world
說明:
NSInteger是 primitive type ,變數是存「值」、NSString是存「記憶體位置」
因此在 myBlock 宣告的時候,各自 capture 這兩個值(1 和 myString 的記憶體位置)
於是在 block 外變更這兩個數值之後再印出,captured 的 1 和 myString 的記憶體位置都不會變動,但是 myString 記憶體位置所存的內容改了,才會印出 Hello, world
Swift
1, Hello
2, Hello, world!
說明:
在 Swift 的 closure 的情形之下, closure 會即時去 capture 執行時變數 當下 的數值。
在執行第一次 myBlock() 的時候,變數值是 1 和 Hello ,
接著執行第二次前 myBlock() 的時候,變數值就已經被變動成 2 和 Hello, world! ,這兩個值就會被 myBlock() capture 進去用
結語 - 注意使用
知道 Swift 的 data type 基本上都變成 struct 之後,就馬上來嘗試這段 code 的結果會不會不同,於是和預想一樣結果不同了。
兩個很類似的東西即使寫法類似,在不同的語言之下執行的結果會不相同的時候,實作上就可以注意一下執行結果會不會因為特性改變而影響。