How to call non-escaping closure inside a local cl

2019-05-09 13:33发布

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?

1条回答
\"骚年 ilove
2楼-- · 2019-05-09 14:39

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
}
查看更多
登录 后发表回答