Swift 3:closures使用非转义参数可能会让它逃脱

我有以下function,我有完成处理程序,但我得到这个错误:

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文档中 ,

当闭包被作为parameter passing给函数时,闭包被认为是转义函数,但在函数返回后被调用。 当你声明一个将闭包作为其参数的函数时,你可以在参数的types之前写入@escaping来指示允许闭包。

TLDR; 在完成variables之后添加@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() } } 

一个“逃避”的封闭是一个封闭,它可以超越它创build的范围。逃逸封闭需要特别注意引用计数和内存pipe理,并且可能更难以优化。

在Swift 3之前,closures的默认设置是假设他们正在逃跑。 这意味着开发人员必须明确识别已知不能转义的闭包,以便编译器进行优化。 社区发现,事实上,编译器可以很容易地发现闭包是否逃跑,并决定采取积极的逃避方法可能会导致更快的代码。 结果是现在假设闭包是非转义的,并且您需要标记使用@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 escapetypes的错误, 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) } 

主要意味着dispatchLater方法需要@escaping ,一旦你添加了, dispatchSometime方法需要@escaping来编译。

 dispatchSometime( { print("Oh yeah") }) func dispatchSometime(_ block: @escaping ()->()) { dispatchLater(block) } func dispatchLater(_ block: @escaping ()->()) { DispatchQueue.main.async(execute: block) } 

然而,拿走只是:

  • 继续添加@escaping的调用链,直到编译器停止抱怨。
  • 关键字不会改变任何内容:这是一个警告,基本上说,“要小心使用捕获的variables,因为它们可能与块本身一起保留。

启示

真正有趣的情况是,你必须调整几个方法来包含@escaping关键字,这样编译器才能停止抱怨。 但是,如果这些方法实际上符合协议,则该协议的方法必须获得@escaping关键字,这也会感染所有其他协议一致性。 有趣!