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

倖福魔咒の 提交于 2020-01-02 05:29:26

问题


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:


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
}


来源:https://stackoverflow.com/questions/40072918/how-to-call-non-escaping-closure-inside-a-local-closure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!