Swift可选的转义闭包

Bil*_*lal 11 closures optional swift

在此输入图像描述

编译器错误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()
    }
}
Run Code Online (Sandbox Code Playgroud)

但如果我使闭包可选,那么没有编译错误,为什么呢?函数返回后仍然可以调用闭包.

func sync(completion:(()->())?) {
    self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
        completion?()
    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*ier 20

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

  • 迷人。我想这是因为,作为包装器内的属性值被分配,它已经被转义了。 (4认同)
  • @matt 没错。如果您在结构体中插入一个闭包,它也会自动“转义”。 (2认同)

Ahm*_*d F 13

澄清:

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

typealias completion = () -> ()

enum CompletionHandler {
    case success
    case failure

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

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler.handler = handlerParameter
}
Run Code Online (Sandbox Code Playgroud)

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

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

let chObject = CompletionHandler.handler = handlerParameter

注意:

注意:参数'handlerParameter'是隐式非转义的func doSomething(handlerParameter:completion){

这是为什么?假设代码片段与@escaping... 无关

实际上,由于Swift 3已经发布,如果默认情况下在enum,structclass中声明它,它将被"转义" .

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

虽然他们可能并非100%与此案有关,但受让人的评论清楚地描述了这种情况:

第一条评论:

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

第二条评论:

不幸的是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)
}
Run Code Online (Sandbox Code Playgroud)

结构案例:

typealias completion = () -> ()

struct CompletionHandler {
    var handler: completion
}

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler(handler: handlerParameter)
}
Run Code Online (Sandbox Code Playgroud)

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

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

func doSomething( handlerParameter: @escaping completion)
Run Code Online (Sandbox Code Playgroud)


回到主要问题:

因为你期望你必须让它completion:(()->())?被转义,这将自动完成 - 如上所述 - .