简体   繁体   中英

Swift optional escaping closure

在此处输入图片说明

Compiler error Closure use of non-escaping parameter 'completion' may allow it to escape , Which make sense because it will be called after the function return.

func sync(completion:(()->())) {
    self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
        completion()
    }
}

But if I make closure optional then no compiler error, Why is that? closure can still be called after the function returns.

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

Wrapping a closure in an Optional automatically marks it escaping. It's technically already "escaped" by being embedded into an enum (the Optional).

Clarification:

For understanding the case, implementing the following code would be useful:

typealias completion = () -> ()

enum CompletionHandler {
    case success
    case failure

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

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

At the first look, this code seems to be legal, but it's not! you would get compile-time error complaining:

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) {

Why is that? the assumption is that the code snippet has nothing to do with the @escaping ...

Actually, since Swift 3 has been released, the closure will be "escaped" if it's declared in enum , struct or class by default.

As a reference, there are bugs reported related to this issue:

Although they might not 100% related to this case, the assignee comments are clearly describe the case:

First comment :

The actual issue here is that optional closures are implicitly @escaping right now.

Second comment :

That is unfortunately the case for Swift 3. Here are the semantics for escaping in Swift 3:

1) Closures in function parameter position are non-escaping by default

2) All other closures are escaping

Thus, all generic type argument closures, such as Array and Optional , are escaping.

Obviously, Optional is enum.

Also -as mentioned above-, the same behavior would be applicable for the classes and structs:

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)


Back to the Main Question:

Since you are expecting that you have to let the completion:(()->())? to be escaped, that would automatically done -as described above-.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM