本文是自己的加深理解和記憶的筆記,非原創(chuàng)。按照自己的理解習(xí)慣改寫了其他文章的內(nèi)容(引用資料在最下方),看看就好,最后強(qiáng)調(diào)一次,非原創(chuàng)。
[TOC]
1. 什么是逃逸閉包?如何標(biāo)記?
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.
即:作為一個傳入?yún)?shù),若該閉包在函數(shù)返回后才被執(zhí)行的話,則該閉包就是在逃逸函數(shù)。(這樣的閉包就是逃逸閉包。)你需要在參數(shù)前加上@escaping標(biāo)記來表明閉包是逃逸的。
2. 什么情況下使用逃逸閉包標(biāo)記?
-
函數(shù)外存儲
如果一個函數(shù)參數(shù)可能導(dǎo)致引用循環(huán),那么它需要被顯示地標(biāo)記出來。@escaping標(biāo)記可以作為一個警告,來提醒使用這個函數(shù)的開發(fā)者注意引用關(guān)系。
舉個例子。此時的
callback
被self
所持有,典型的可能在函數(shù)return之后被執(zhí)行。class SomeClass { var callback:(()->Void)? func doSomething(callback:@escaping ()->Void) { // 加上逃逸修飾詞 self.callback = callback } }
-
異步調(diào)用
同理,如果閉包被放進(jìn)
async dispatch queue
,則該閉包也會被queue retain
,同樣可能在函式結(jié)束后才被執(zhí)行,因此也算是“逃逸”舉個例子。此時的callback被異步調(diào)用了
class SomeClass { func doWorkAsync(block: @escaping () -> ()) { // 加上逃逸修飾詞 DispatchQueue.main.async { block() } } }
3. 非逃逸閉包有什么限制
不能在函式外儲存
不能進(jìn)
async dispatch queue
-
不能作為其他逃逸閉包函數(shù)的參數(shù)
把@noescape閉包傳到其他@noescape參數(shù)是可以的,一連串不會逃逸的傳值,最終還是不會逃逸(下面的@noescape會被編譯器提示刪除,因?yàn)閟wift3開始默認(rèn)的就是非逃逸閉包)
class SomeClass { func foo( code:@noescape (() -> String)) -> String { return bar(code: code) } func bar( code:@noescape (() -> String)) -> String { return code() } }
4.其他:
從swift3開始,閉包默認(rèn)為非逃逸閉包。之前則相反,且使用@noescape進(jìn)行標(biāo)記(此標(biāo)記已廢棄)。
非逃逸閉包可用被編譯器高度優(yōu)化,快速的執(zhí)行路徑將被作為基準(zhǔn)而使用,除非你在有需要的時候顯式地使用其他方法。
和弱引用關(guān)系:非逃逸閉包中可放心使用self關(guān)鍵字,因?yàn)椴粫诤瘮?shù)外儲存,也不會被異步調(diào)用。你不需要去使用一個弱引用(weak或unowned)去引用self。
-
在函數(shù)內(nèi)部儲存閉包也會被識別成逃逸,雖然并不會(現(xiàn)在的最新swift4仍存在這個問題)
func doSomething(callback:(()->Void) { let c = callback // error: non-escaping parameter 'callback' may only be called c() }