Reputation: 19156
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?()
}
}
Upvotes: 16
Views: 10086
Reputation: 31685
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:
The actual issue here is that optional closures are implicitly @escaping right now.
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)
Since you are expecting that you have to let the completion:(()->())?
to be escaped, that would automatically done -as described above-.
Upvotes: 22
Reputation: 299663
Wrapping a closure in an Optional automatically marks it escaping. It's technically already "escaped" by being embedded into an enum (the Optional).
Upvotes: 28