閉包只有在函數中做參數的時候才會區分逃逸閉包和非逃逸閉包
在Swift 3 後,傳遞閉包到函數中的時候,系統會預設為非逃逸閉包類型 (Nonescaping Closures)@noescape,有非逃逸閉包類型必然就有逃逸閉包(Escaping Closures),逃逸閉包在閉包前要添加@escaping關鍵字
非逃逸閉包的生命週期:1.把閉包作為參數傳給函數;2.函數中調用閉包;3.退出函數,閉包生命週期結束
即非逃逸閉包的生命週期與函數相同
逃逸閉包的生命週期:1.閉包作為參數傳遞給函數;2.退出函數; 3.閉包被調用,閉包生命週期結束
即逃逸閉包的生命週期長於函數,函數退出的時候,逃逸閉包的引用仍被其他對象持有,不會在函數結束時釋放
例子:
非逃逸閉包:
import UIKit
class HttpTool: NSObject {
1 func loadData(callBack:((String)->())){
2 callBack("非逃逸閉包")
3 }
}
ViewController類:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var tools:HttpTool = HttpTool()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
tools.loadData { (jsonData:String) in
print(jsonData);
}
}
}
代碼執行順序1,2,3
當傳遞閉包參數給函數loadData時,要注意ViewController中的屬性tools,雖然閉包會捕獲self,但是由於預設閉包參數是非逃逸型,這裡可以省略 self, 反正編譯器已經知道這裡不會有循環參考的潛在風險。
逃逸閉包:
import UIKit
class HttpTool: NSObject {
func loadData(callBack:@escaping((String)->())){
1 DispatchQueue.global().async {
DispatchQueue.main.async {
2 callBack("這是逃逸閉包")
}
}
3 }
}
ViewController類:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
var tools:HttpTool = HttpTool()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
tools.loadData { (jsonData:String) in
print(jsonData);
}
}
}
代碼執行順序1,3,2
當傳遞閉包參數給函數loadData時,要注意ViewController中的屬性tools,這裡閉包函數的生命週期在函數結束後結束,tools前面省略的self 就有必要做特殊處理,防止造成死迴圈。
逃逸閉包在閉包前要添加@escaping關鍵字,這裡的閉包的生命週期不可預知
經常使用逃逸閉包的2個情境: 非同步呼叫: 如果需要調度隊列中非同步呼叫閉包,比如網路請求成功的回調和失敗的回調,這個隊列會持有閉包的引用,至於什麼時候調用閉包,或閉包什麼時候運行結束都是不確定,上邊的例子。 儲存: 需要儲存閉包作為屬性,全域變數或其他類型做稍後使用,例子待補充。
以後有用到逃逸閉包的例子,會及時更新,也歡迎看見的小夥伴幫忙補充