本文翻譯自這里
函數與閉包在Swift中作為一等公民,可以存儲持偏,當作參數傳遞驼卖,并且把它們看待成其他的對象或者類型。通常我們將閉包當作發(fā)送API完成后的回調鸿秆。
在Swift3中酌畜,當你將閉包當作一個函數的參數時,會有一個新的建議:編譯器將閉包默認為non-escaping
卿叽。下面看看non-escaping
和eascaping
的區(qū)別桥胞。
Non-Escaping Closures
non-escaping的生命周期:
1.將閉包傳遞給函數
2.函數執(zhí)行閉包(或不執(zhí)行)
3.函數返回
這個閉包并沒有“逃逸(escape)”到函數體外恳守。當函數結束時,傳遞的閉包離開函數作用域贩虾,并且沒有其他的引用指向該閉包催烘。
如果考慮到內存的持有和釋放平衡,這個閉包的引用計數在函數結束時和開始時是一樣的缎罢。
Escaping Closures
現(xiàn)在可以猜到escaping closure
是什么意思伊群。在函數內,你可以一直運行閉包(或者不)策精;這里有幾種方法來讓閉包逃逸出函數體:
異步操作:如果在一個異步隊列中執(zhí)行閉包舰始,那么這個隊列會一直持有這個閉包。你無法確定閉包何時被執(zhí)行咽袜,并且也無法保證在函數返回前結束閉包丸卷。
存儲:將閉包存儲為全局變量、屬性询刹、或者任何其他的存儲方式谜嫉。
Escaping and Non-Escaping in Swift 3
在Swift1和2中,閉包參數默認為escaping
范抓。如果能夠確保閉包沒有逃逸出函數體,你可以使用@noescape
修飾它食铐。
在Swift3中有些不同匕垫,參數閉包默認修飾為non-escaping
。
如果閉包修飾為non-escaping
虐呻,這里有一些潛在的優(yōu)化象泵。因為閉包不會逃逸,編譯器可以將閉包的存儲和調用優(yōu)化斟叼。
經常碰到的情況就是閉包中持有self的時候:
class ClassA {
// takes a closure (non-escaping by default)
func someMethod(closure: () -> Void) {
// secret stuff
}
}
class ClassB {
let classA = ClassA()
var someProperty = "Hello"
func testClosure() {
classA.someMethod {
// self is captured!
someProperty = "Inside the closure!"
}
}
}
當調用someMethod
的時候偶惠,記住someProperty
是ClassB
的成員屬性。只有閉包為逃逸閉包的時候才必須使用self
朗涩,而這段代碼在Swift3中運行不會有任何問題忽孽。
這個閉包仍然會截獲self
,但是因為閉包在函數結束后就會釋放谢床,所以編譯器會知道沒有發(fā)生循環(huán)引用兄一。
如果將代碼改成如下這樣:
func someMethod(closure: @escaping () -> Void) {
// secret stuff
}
現(xiàn)在這變成另外一種情況,當調用這個方法并且提供一個有引用指向的閉包识腿,在閉包內必須明確使用self
來提醒自己這個閉包截獲了當前的self
出革。
最后
在Swift3中,閉包參數默認為non-escaping渡讼,根據自己的需求使用@escaping
骂束。非逃逸閉包當做參數傳遞時耳璧,在函數返回之前閉包必須執(zhí)行完。