本文是自己的加深理解和記憶的筆記挂滓,非原創(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.
即:作為一個(gè)傳入?yún)?shù)曾沈,若該閉包在函數(shù)返回后才被執(zhí)行的話这嚣,則該閉包就是在逃逸函數(shù)。(這樣的閉包就是逃逸閉包塞俱。)你需要在參數(shù)前加上@escaping標(biāo)記來(lái)表明閉包是逃逸的姐帚。
2. 什么情況下使用逃逸閉包標(biāo)記?
-
函數(shù)外存儲(chǔ)
如果一個(gè)函數(shù)參數(shù)可能導(dǎo)致引用循環(huán)障涯,那么它需要被顯示地標(biāo)記出來(lái)罐旗。@escaping標(biāo)記可以作為一個(gè)警告膳汪,來(lái)提醒使用這個(gè)函數(shù)的開發(fā)者注意引用關(guān)系。
舉個(gè)例子九秀。此時(shí)的
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
痹换,則該閉包也會(huì)被queue retain
,同樣可能在函式結(jié)束后才被執(zhí)行友酱,因此也算是“逃逸”舉個(gè)例子晴音。此時(shí)的callback被異步調(diào)用了
class SomeClass { func doWorkAsync(block: @escaping () -> ()) { // 加上逃逸修飾詞 DispatchQueue.main.async { block() } } }
3. 非逃逸閉包有什么限制
不能在函式外儲(chǔ)存
不能進(jìn)
async dispatch queue
-
不能作為其他逃逸閉包函數(shù)的參數(shù)
把@noescape閉包傳到其他@noescape參數(shù)是可以的,一連串不會(huì)逃逸的傳值缔杉,最終還是不會(huì)逃逸(下面的@noescape會(huì)被編譯器提示刪除,因?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)而使用,除非你在有需要的時(shí)候顯式地使用其他方法昭伸。
和弱引用關(guān)系:非逃逸閉包中可放心使用self關(guān)鍵字梧乘,因?yàn)椴粫?huì)在函數(shù)外儲(chǔ)存,也不會(huì)被異步調(diào)用庐杨。你不需要去使用一個(gè)弱引用(weak或unowned)去引用self选调。
-
在函數(shù)內(nèi)部?jī)?chǔ)存閉包也會(huì)被識(shí)別成逃逸,雖然并不會(huì)(現(xiàn)在的最新swift4仍存在這個(gè)問(wèn)題)
func doSomething(callback:(()->Void) { let c = callback // error: non-escaping parameter 'callback' may only be called c() }