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 escape
types的错误, 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
关键字,这也会感染所有其他协议一致性。 有趣!