I have a function which looks something like this:
func test(closure: () -> ()) {
let localClosure = { closure() }
localClosure()
}
This is only an example and does not fully reflect the problem I encountered, obviously here I could have just called closure
directly!
It should be clear that in the above code, closure
cannot escape. However, I get the error:
Closure use of non-escaping parameter 'closure' may allow it to escape
Now, if localClosure
was escaping in some way, I'd understand this error, but it doesn't escape. I even tried annotating localClosure
as @noescape
(even though that attribute is deprecated in Swift 3), and according to the warning I got:
@noescape is the default and is deprecated
If localClosure
is, by default, non-escaping, then why can't another non-escaping closure go inside it? Or is this a bug/limitation of the compiler?
An escaping closure is a closure that's called after the function it was passed to returns. In other words, it outlives the function it was passed to. A non-escaping closure is a closure that's called within the function it was passed into, i.e. before it returns.
In Swift, closures are non-escaping by default. If you pass a non-escaping closure into a function, it is called right away. The life of the non-escaping closure ends when the function call finishes. As a result, there will be no trace of that closure.
“A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns.” In order to be an escaping closure, the function to which the closure is passed must return before the closure is executed.
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
@escaping
, by default"If
localClosure
is, by default, non-escaping, then why ..."
Based on the discussion in the comments below (thanks @Hamish), we can state the following facts regarding non-parameter closures† in Swift 3.0:
@escaping
, by default. As @noescape
is deprecated in Swift 3 (see e.g. Xcode 8 release notes or Swift evolution proposal SE-0103), this means that non-parameter closures cannot be made non-escaping without making use of deprecated methods.@noescape
attribute for non-parameter closures is a missing feature (somewhat of a regression as this was not a limitation in Swift 2.2), but one that is not necessarily to be implemented in the future (if I'm to understand the answer by Apple dev. Jordan Rose in the linked evolution thread).We may however (still) apply the deprecated @noescape
attribute to a non-parameter closure to make it non-escaping, but will then notably be prompted with an incorrect warning (as below, emphasis mine), which has now been reported as a bug by @Hamish, see bug report SR-2969.
"
@noescape
is default and is deprecated"
To summarize, localClosure
is @escaping
, which naturally means it cannot be allowed to wrap the non-escaping closure parameter closure
of test(...)
.
[†] By non-parameter closures, I refer to all closures that are not parameters to a function, i.e., closures that are not supplied to a function as an argument.
As a side-note, which you possibly already knows given your question: we may naturally mark closure
as @escaping
if we'd wish to process/wrap it as in your example.
func test(closure: @escaping () -> ()) -> () -> () {
let escapingLocalClosure = { closure() }
return escapingLocalClosure
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With