Swift可选的转义闭包

时间:2017-11-21 16:25:04

标签: swift closures optional

enter image description here

编译器错误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?()
    }
}

2 个答案:

答案 0 :(得分:17)

在Optional中包装闭包会自动将其标记为转义。它在技术上已经逃脱了#34;通过嵌入枚举(可选)。

答案 1 :(得分:10)

澄清:

为了理解这种情况,实现以下代码会很有用:

typealias completion = () -> ()

enum CompletionHandler {
    case success
    case failure

    static var handler: completion {
        get { return { } }
        set { }
    }
}

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler.handler = handlerParameter
}

初看起来,这段代码似乎合法,但事实并非如此!你会得到编译时错误抱怨:

  

错误:分配非转义   参数' handlerParameter'到@escaping闭包

     

让chObject = CompletionHandler.handler = handlerParameter

注意:

  

注意:参数' handlerParameter'隐含地是非转义函数   doSomething(handlerParameter:completion){

为什么?假设代码片段与@escaping ...

无关

实际上,由于Swift 3已经发布,关闭将被"逃脱"如果它默认在 enum struct class 中声明。

作为参考,报告了与此问题相关的错误:

尽管他们可能并非100%与此案有关,但受让人的评论清楚地描述了案例:

First comment

  

这里的实际问题是可选闭包是隐含的   @escaping现在。

Second comment

  

不幸的是Swift 3的情况。这是语义   逃离Swift 3:

     

1)功能参数位置的闭包是   默认情况下不转义

     

2)所有其他关闭都在逃避

     

因此,所有泛型类型的参数闭包,例如Array和 Optional 都在转义。

显然,Optional是枚举。

同样 - 如上所述 - 同样的行为适用于类和结构:

类案例:

typealias completion = () -> ()

class CompletionHandler {
    var handler: () -> ()

    init(handler: () -> ()) {
        self.handler = handler
    }
}

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler(handler: handlerParameter)
}

结构案例

typealias completion = () -> ()

struct CompletionHandler {
    var handler: completion
}

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler(handler: handlerParameter)
}

上面两个代码片段会导致相同的输出(编译时错误)。

要修复案例,您需要让函数签名为

func doSomething( handlerParameter: @escaping completion)


返回主要问题:

由于您预计必须让completion:(()->())?转义,这将自动完成 - 如上所述 - 。