天天看點

Swift中的@escaping是什麼?

由donnywals于2020年3月11日釋出

如果您曾經編寫或使用過将閉包(閉包的使用:《Swift語言入門執行個體教程》課程第6章第5節:Swift中的閉包(Closure)詳解)作為其參數之一的函數,則很可能遇到了@escaping關鍵字。 當閉包在Swift中被标記為escaping(轉義)時,這意味着該閉包将失效,或者保留傳遞給它的作用域。 讓我們看一個非轉義閉包的例子:

func doSomething(using closure: () -> Void) {
  closure()
}
           

傳遞給doSomething(using :)的閉包在doSomething(using :)函數中立即執行。 因為閉包是在doSomething(using :)範圍内立即執行的,是以我們知道在閉包内部所做的任何事情都不會洩漏或超過doSomething(using :)範圍的壽命。 如果我們在doSomething(using :)中進行關閉操作或保留函數作用域,則會收到編譯器錯誤:

func doSomething(using closure: () -> Void) {
  DispatchQueue.main.async {
    closure()
  }
}
           

上面的代碼将導緻編譯器錯誤,因為Swift現在可以看到,當我們調用doSomething(using :)時,傳遞給該函數的閉包将轉義其範圍。這意味着我們需要将其标記為故意的,是以doSomething(using :)的調用者将知道他們正在處理一個閉包,該閉包将超過傳遞給它的函數的範圍,這意味着他們需要采取措施預防循環引用或記憶體洩露。除了向調用者告知轉義閉包doSomething(using :)之外,它還告訴Swift編譯器我們知道閉包保留了傳遞給它的作用域,對此我們可以接受。

您通常會看到轉義閉包以執行異步工作并将函數作為回調調用閉包。例如,URLSession.dataTask(with:completionHandler :)的completionHandler标記為@escaping,因為在請求完成後即完成資料處理程式後即建立資料任務之後的某個時間,由于完成處理程式傳遞的閉包被執行。如果您編寫的代碼帶有完成處理程式并使用資料任務,則您接受的閉包也必須标記為@escaping。

func makeRequest(_ completion: @escaping (Result<(Data, URLResponse), Error>) -> Void) {
  URLSession.shared.dataTask(with: URL(string: "https://donnywals.com")!) { data, response, error in
    if let error = error {
      completion(.failure(error))
    } else if let data = data, let response = response {
      completion(.success((data, response)))
    }

    assertionFailure("We should either have an error or data + response.")
  }
}
           
請注意,在上方的結束代碼中,标記為@escaping,是因為我在資料任務的完成處理程式中使用了它。 這意味着,在makeRequest(_ :)退出作用域之前,不會執行completion閉包,并且completion閉包的生命周期更長。

簡而言之,@escaping用于通知采取閉包的函數的調用者,該閉包可能已存儲或超出了接收函數的範圍。 這意味着調用方必須采取預防措施,以防止循環引用和記憶體洩漏。

譯自:https://www.donnywals.com/what-is-escaping-in-swift/

繼續閱讀