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 的結果會不會不同,於是和預想一樣結果不同了。
兩個很類似的東西即使寫法類似,在不同的語言之下執行的結果會不相同的時候,實作上就可以注意一下執行結果會不會因為特性改變而影響。