看一段OC代碼
NSInteger i = 1;
void(^block)(void) = ^{
NSLog(@"block %ld:", i);
};
i += 1;
NSLog(@"out1 %ld:", i);
block();
NSLog(@"out2 %ld:", i);
打印

oc.png
在OC里,編譯器走到第三行的時(shí)候,實(shí)際上已經(jīng)對(duì)i進(jìn)行了拷貝,可以理解成
NSInteger iCopy = i
所以block里的值和外面的值是不會(huì)互相影響的。
如果想要里外一致,則需要通過添加 __block關(guān)鍵字,block里使用的變量不管是基本數(shù)據(jù)列行還是類,都會(huì)自動(dòng)封裝成一個(gè)對(duì)象(結(jié)構(gòu)體),這個(gè)對(duì)象擁有isa指針和forwarding指針,forwarding指針指向的是自己,在ARC下block里有用到外部局部變量則會(huì)自動(dòng)copy到堆,在棧區(qū)的forwarding指針就指向到堆里的isa(堆里的forwarding還是指向自己),這就打通了內(nèi)外,這里不詳細(xì)討論此關(guān)鍵字以及循環(huán)引用相關(guān)。
看一段Swift代碼
var i = 1
let closure = {
print("closure \(i)")
}
i += 1
print("out1 \(i)")
closure()
print("out 2 \(i)")
代碼和OC一毛一樣,打印的結(jié)果是

swift
swift指的捕獲是在執(zhí)行的時(shí)候再捕獲,當(dāng)代碼執(zhí)行到 closure(),對(duì)值進(jìn)行捕獲,i的值是2,所以打印閉包里的i等于2
修改值
在OC里修改值基本數(shù)據(jù)類型__block, OC-Obj類型__weak。
在Swift里,看如下代碼
var i = 1
let closure = {
i += 1
print("closure \(i)")
}
i += 1
print("out1 \(i)")
closure()
print("out 2 \(i)")
在閉包里多了一行 i += 1 編譯,沒有警告,運(yùn)行結(jié)果如下

image.png
也就是說,在swift里,閉包就像是oc給外部變量默認(rèn)添加了block或者__weak
Swift里實(shí)現(xiàn)和OC一樣的值捕獲,這種實(shí)現(xiàn)在Swift里叫捕獲列表,capturing list
看如下代碼
var i = 1
let closure = {
[i] in
print("closure \(i)")
}
i += 1
print("out1 \(i)")
closure()
print("out 2 \(i)")
閉包內(nèi)部多了一行[i] in 語(yǔ)法為中括號(hào)[]里面添加捕獲的變量,然后用in 分割上下分。打印結(jié)果為

image.png
類似于剛開始的OC代碼,Swift內(nèi)部,類似執(zhí)行了這樣的一行代碼
let iCopy = i
最后
大家都說Swift里的閉包和OC里的block一樣,在值的捕獲這里是有很大的不一樣的。