使用閉包,有兩種情況,一種是在呼叫者完成前閉包就被執行完成了。還有一種則相反:呼叫函式完成了,但是閉包還沒有被呼叫或者沒有被完成。後者被稱為逃逸閉包。
所有網路請求的函式,在完成呼叫請求後,直到響應返回,閉包才會被呼叫,所以這個型別的網路請求函式內等待響應的閉包就是逃逸閉包。這個型別的閉包,需要程式設計師手工加入一個@escaping標記才可以編譯通過。
如下程式碼,展示了一個非逃逸閉包,和一個逃逸閉包。後者已經被標記了@escapings:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window : UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func syncRequest(callBack: ()->Void ) {
callBack()
}
func asyncRequest( callBack: @escaping()->Void ) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
callBack()
})
}
syncRequest(){
print("callback")
}
asyncRequest(){
print("delay 1s callback")
}
window = UIWindow()
window!.rootViewController = UIViewController()
window!.rootViewController!.view.backgroundColor = .blue
window!.makeKeyAndVisible()
return true
}
}複製程式碼
函式DispatchQueue.main.asyncAfter用來延時。此處延時1s再呼叫callback,演示了一個逃逸閉包的效果。
閉包可能需要引用當前上下文的變數,因此當呼叫者完成後,如果標記了逃逸閉包,那麼當前呼叫的上下文依然會保持。如果在該標記的地方沒有標記的話,會怎麼樣?不會在執行時報錯,而是在編譯期間就報錯了。
因為編譯器知道你沒有立即呼叫callback。好智慧。
然而,既然知道這種情況下,我需要標記@escaping,幹麼不直接做了?我暫時還無法回答這個問題,或者你知道?