编译器错误Closure use of non-escaping parameter ‘completion’ may allow it to escape,这是有道理的,因为它将在函数 返回之后被调用。
func sync(completion:(()->())) { self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in completion() } }
但是,如果我将闭包设为可选,则不会出现编译器错误,那是为什么呢? 函数返回后仍可以调用闭包。
func sync(completion:(()->())?) { self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in completion?() } }
为了理解这种情况,实现以下代码将很有用:
typealias completion = () -> () enum CompletionHandler { case success case failure static var handler: completion { get { return { } } set { } } } func doSomething(handlerParameter: completion) { let chObject = CompletionHandler.handler = handlerParameter }
乍一看,此代码似乎合法,但事实并非如此!您会收到 编译时错误抱怨:
error : assigning non-escaping parameter ‘handlerParameter’ to an @escaping closure let chObject = CompletionHandler.handler = handlerParameter
error : assigning non-escaping parameter ‘handlerParameter’ to an @escaping closure
let chObject = CompletionHandler.handler = handlerParameter
with a note that:
note : parameter ‘handlerParameter’ is implicitly non-escaping func doSomething(handlerParameter: completion) {
这是为什么?假设是代码段已无关的@escaping…
实际上,由于Swift 3已发布,如果默认在enum,struct或class中声明了闭包,则闭包将被“转义” 。
As a reference, there are bugs reported related to this issue:
尽管它们可能与本案并非100%相关,但受让人的评论 清楚地描述了该案:
First comment:
The actual issue here is that optional closures are implicitly @escaping right now.
Second comment:
不幸的是,Swift 3就是这种情况。这是 Swift 3中转义的语义:
1)默认情况下,函数参数位置的闭包不转义
2)所有其他关闭都在逃逸
因此,所有通用类型参数闭包(例如Array和Optional) 都在转义。
Obviously,Optional is enum.
Optional
同样-如上所述,相同的行为也适用于 类和结构:
Class Case:
typealias completion = () -> () class CompletionHandler { var handler: () -> () init(handler: () -> ()) { self.handler = handler } } func doSomething(handlerParameter: completion) { let chObject = CompletionHandler(handler: handlerParameter) }
Struct Case:
typealias completion = () -> () struct CompletionHandler { var handler: completion } func doSomething(handlerParameter: completion) { let chObject = CompletionHandler(handler: handlerParameter) }
The two above code snippets would leads to the same output (compile-time error).
For fixing the case, you would need to let the function signature to be :
func doSomething( handlerParameter: @escaping completion)
既然你期待,你必须让completion:(()->())?被逃脱,会自动完成-as上面描述。