前言
Swift的閉包在capture變量時(shí)隅熙,跟oc的還是有些區(qū)別的遍愿。下面來(lái)講講奔滑。
一覽Swift的閉包
在closure中使用外部變量耙考,swift默認(rèn)會(huì)capture該變量的reference
谜喊,之后會(huì)細(xì)說(shuō)。為了避免在閉包執(zhí)行時(shí)琳骡,變量釋放锅论,所以會(huì)retain該變量讼溺。來(lái)看個(gè)例子楣号。
Animal類(lèi)。
class Animal:CustomDebugStringConvertible {
let name: String
init(name: String) {
self.name = name
}
var debugDescription: String {
return "<Animal \(name)>"
}
deinit {
print("\(self) dealloc")
}
}
delay函數(shù)
func delay(seconds: NSTimeInterval, closure: ()->()) {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
closure()
}
}
func demo1() {
let animal = Animal(name: "cat")
print("before closure:\(animal)")
delay(1) {
// retain animal
print("inside closure:\(animal)")
}
print("bye")
}
output:
before closure:<Animal cat>
bye
inside closure:<Animal cat>
<Animal cat> dealloc
這里怒坯,我們用dispatch_after延遲執(zhí)行一個(gè)閉包炫狱。當(dāng)demo1執(zhí)行完后,animal是還沒(méi)有dealloc的剔猿,因?yàn)楸婚]包retain了视译。只有當(dāng)閉包執(zhí)行完后,才會(huì)釋放掉归敬。
好了酷含。知道了上述理論后,來(lái)看看剛剛提到的reference汪茧。
reference
閉包默認(rèn)持有變量的reference椅亚,而不是變量本身。什么意思呢舱污?看個(gè)例子呀舔。請(qǐng)注意inside closure打印的那行
。
// caputure value evaluted when closure is executed
func demo2() {
var animal = Animal(name: "cat")
print("before closure:\(animal)")
delay(1) {
print("inside closure:\(animal)")
}
animal = Animal(name: "dog")
print("after closure:\(animal)")
}
ouput:
before closure:<Animal cat>
<Animal cat> dealloc
after closure:<Animal dog>
inside closure:<Animal dog>
<Animal dog> dealloc
是不是覺(jué)得有點(diǎn)奇怪扩灯?這就是持有reference的作用媚赖。
可以看成其持有的是指向animal的指針p,即使animal所指向的obj在變珠插,但其實(shí)p的指針是始終不變的惧磺。看2張圖應(yīng)該就明白了捻撑。
p就是圖中的reference豺妓。
如果熟悉c的指針的話(huà)惜互,跟下面的代碼意思類(lèi)似。
char a = 'a';
char *p1 = &a;
char **p2 = &p1;
printf("%c", **p2);
char b = 'b';
p1 = &b;
// 此時(shí)**p2的值會(huì)為'b'琳拭,雖然p1的指向在變训堆,但p2-->p1的指向關(guān)系始終不變
printf("%c", **p2);
除了reference之外,還有提到的是白嘁。
1)capture的變量在閉包執(zhí)行的時(shí)候才計(jì)算
坑鱼。
在這個(gè)例子中,我們修改了animal為dog絮缅,閉包中打印的也是成了dog鲁沥。
如果改成這樣,猜猜會(huì)打印出什么耕魄?
delay(1) {
animal = Animal(name:"duck")
print("inside closure:\(animal)")
}
- animal的dealloc画恰。在demo2執(zhí)行完后,cat就dealloc了吸奴。而dog還被持有允扇。看上圖则奥,animal--->cat之前的關(guān)聯(lián)已經(jīng)斷了考润,所以會(huì)dealloc,而此時(shí)animal指向了dog读处,所以dog仍會(huì)等到閉包執(zhí)行完后銷(xiāo)毀糊治。
value type
那么對(duì)于值類(lèi)型的捕獲呢,是否不一樣罚舱?很遺憾井辜,仍然是reference。意思是:不是簡(jiǎn)單的捕獲當(dāng)前的值管闷,存起來(lái)粥脚,亙古不變了。跟上面一樣渐北,value變了之后阿逃,閉包中的值跟著變。
func demo3() {
var a = 40
print("before closure:\(a)")
delay(1) {
print("inside closure:\(a)")
}
a = 50
print("after closure:\(a)")
}
output:
before closure:40
after closure:50
inside closure:50
在閉包中修改值
可以在直接閉包中修改捕獲的值赃蛛,并且后續(xù)獲取到的就是新值恃锉。仍然是reference的功勞。而在oc中呕臂,需要聲明為_(kāi)_block破托。
func demo4() {
var a = 40
print("before closure:\(a)")
delay(1) {
print("inside closure1, before change:\(a)")
a = 60
print("inside closure1, after change:\(a)")
}
delay(2) {
print("inside closure2:\(a)")
}
}
如何做到和oc的捕獲一樣
如果只想capture變量在創(chuàng)建閉包時(shí)的值,使用capture list歧蒋。它會(huì)copy一份變量土砂,而非采用reference的方式州既。或者先聲明個(gè)變量 let copyValue = a萝映,跟capture list效果一樣吴叶。
func demo5() {
var a = 40
print("before closure:\(a)")
let copyValue = a
delay(1) { [constantValue = a] in
print("inside closure:\(constantValue)")
print("inside closure:\(copyValue)")
}
a = 50
print("after closure:\(a)")
}
同樣對(duì)于class類(lèi)型的,也一樣
func demo6() {
var animal = Animal(name: "cat")
print("before closure:\(animal)")
let animalCopy = animal
delay(1) { [constantAnimal = animal] in
print("inside closure constantAnimal:\(constantAnimal)")
print("inside closure constantAnimal:\(animalCopy)")
}
animal = Animal(name: "dog")
print("after closure:\(animal)")
}
終極混合
來(lái)個(gè)各種混合的序臂。
func demo7() {
var animal = Animal(name: "cat")
print("before closure:\(animal)")
delay(1) { [constantAnimal = animal] in
print("inside closure1 constantAnimal:\(constantAnimal)")
print("inside closure1:\(animal)")
animal = Animal(name: "snake")
print("inside closure1 animal changed:\(animal)")
}
animal = Animal(name: "dog")
print("animal changed:\(animal)")
delay(2) { [constantAnimal = animal] in
print("inside closure2 constantAnimal:\(constantAnimal)")
print("inside closure2:\(animal)")
animal = Animal(name: "bear")
print("inside closure2 animal changed:\(animal)")
}
}
看看會(huì)是神馬結(jié)果蚌卤??
后語(yǔ)
- swift閉包默認(rèn)持有變量的reference
- swift閉包默認(rèn)在執(zhí)行時(shí)才計(jì)算捕獲變量的值
- 可在swift閉包中修改捕獲變量的值
- 使用capture list奥秆,做變量的constant copy捕獲逊彭。