簡體   English   中英

Swift 3:關閉使用非轉義參數可能允許它逃脫

[英]Swift 3 :Closure use of non-escaping parameter may allow it to escape

我有以下功能,我有完成處理程序,但我收到此錯誤:

Closure use of non-escaping parameter may allow it to escape

這是我的代碼:

func makeRequestcompletion(completion:(_ response:Data, _ error:NSError)->Void)  {
    let urlString = URL(string: "http://someUrl.com")
    if let url = urlString {
        let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
            completion(data, error) // <-- here is I'm getting the error
        })
    task.resume()
    }
}

在此輸入圖像描述 你們中的任何人都知道我為什么會收到這個錯誤?

我真的很感謝你的幫助

看起來你需要明確定義允許閉包轉義。

來自Apple Developer docs

當閉包作為參數傳遞給函數時,閉包被稱為轉義函數,但在函數返回后調用。 當您聲明一個以閉包作為其參數之一的函數時,您可以在參數的類型之前編寫@escaping以指示允許閉包轉義。

TLDR; 在完成變量后添加@escaping關鍵字:

func makeRequestcompletion(completion: @escaping (_ response:Data, _ error:NSError)->Void)  {
    let urlString = URL(string: "http://someUrl.com")
    if let url = urlString {
        let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
            completion(data, error) // <-- here is I'm getting the error
        })
        task.resume()
    }
}

“轉義”閉包是一個閉包,它可以比它創建的范圍更長。轉出閉包需要特別注意引用計數和內存管理,並且可能更難以優化。

在Swift 3之前,閉包的默認設置是假設它們正在轉義。 這意味着開發人員必須專門識別已知不會被轉義的閉包,以允許編譯器進行優化。 社區發現事實上,編譯器可以很容易地找出閉包是否轉出,並決定一種積極的轉義方法可以導致更快的代碼。 結果是現在假定閉包是非轉義的,並且您需要標記使用@escaping屬性轉義的閉包。

在您的情況下, URLSession.shared.dataTask接受的閉包本身就是一個轉義閉包,因此如果您在其中使用閉包,則還需要將其標記為@escaping

@escaping對所有調用方法都具有感染力,編譯器確定何時必須包含它。

考慮這個例子(編譯):

dispatchSometime( { print("Oh yeah") })

func dispatchSometime(_ block: ()->()) {
    dispatchNow(block)
}

func dispatchNow(_ block: ()->()) {
    block()
}

但是,此修改示例會產生兩個類型non-escaping parameter may allow it to escape錯誤, non-escaping parameter may allow it to escape

dispatchSometime( { print("Oh yeah") })

func dispatchSometime(_ block: ()->()) {
    dispatchLater(block)
}

func dispatchLater(_ block: ()->()) {
    DispatchQueue.main.async(execute: block)
}

main上的dispatchLater意味着dispatchLater方法需要@escaping ,一旦你添加了它, dispatchSometime方法需要@escaping來編譯示例。

dispatchSometime( { print("Oh yeah") })

func dispatchSometime(_ block: @escaping ()->()) {
    dispatchLater(block)
}

func dispatchLater(_ block: @escaping ()->()) {
    DispatchQueue.main.async(execute: block)
}

然而,帶走只是:

  • 繼續在調用鏈上添加@escaping ,直到編譯器停止抱怨。
  • 關鍵字沒有改變任何東西:它是一個警告,基本上說,“小心使用weak捕獲變量,因為它們可能與塊本身一起保留。”

啟示

這個真正有趣的情況是你需要調整幾個方法來包含@escaping關鍵字,這@escaping編譯器停止抱怨。 但是,如果這些方法實際上符合協議,則該協議的方法必須獲取@escaping關鍵字,該關鍵字也會感染所有其他協議符合性。 有趣!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM