Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call non-escaping closure inside a local closure? [duplicate]

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?

like image 601
Robert Avatar asked Oct 16 '16 17:10

Robert


People also ask

What is the difference between an escaping closure and a non-escaping closure?

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.

What is non-escaping closure in Swift?

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.

What is the difference between @escaping and @nonescaping closures in Swift?

“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.

What is Clouser in Swift?

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.


1 Answers

Non-parameter closures are @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:

  • They are, contrary to what one might believe, @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.
  • As described in the following evolution thread, the lack of @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
}
like image 183
dfrib Avatar answered Sep 22 '22 17:09

dfrib