Updating closures to Swift 3 - @escaping

2019-01-08 07:38发布

I've updated my code to Xcode 8.0 beta 6 but I got stuck with what seems to be about the new non escaping closure default. In the following code Xcode suggests to add @escaping in front of completion: in the first line of the below code, but that still won't compile and goes in circles. *

(EDIT: In fact, @escaping should be added in after completion:, as Xcode suggests. The alert may still show but cleaning and compiling will remove it.)* How should this code be re-written / fixed to work in the updated Swift 3? I've had a look in the new manual but I couldn't find proper code samples.

func doSomething(withParameter parameter: Int, completion: () -> ()) {
    // Does something

    callSomeOtherFunc(withCompletion: completion)
  }

// Calling the method and execute closure 
doSomething(withParameter: 2) {
  // do things in closure
}

Any help much appreciated!

2条回答
兄弟一词,经得起流年.
2楼-- · 2019-01-08 08:04

Swift 3: closure parameter attributes are now applied to the parameter type, and not the parameter itself

Prior to Swift 3, the closure attributes @autoclosure and @noescape used to be attributes to the closure parameter, but are now attributes to the parameter type; see the following accepted Swift evolution proposal:

Your specific question pertain to parameter type attribute @escaping (for which the same new rule applies), as described in the accepted Swift evolution proposal to let closure parameters be non-escaping by default:

These proposals are now both implemented in the beta stage of Xcode 8 (see release notes for Xcode 8 beta 6; dev. account login needed for access)

New in Xcode 8 beta 6 - Swift Compiler: Swift Language

Closure parameters are non-escaping by default, rather than explicitly being annotated with @noescape. Use @escaping to indicate that a closure parameter may escape. @autoclosure(escaping) is now written as @autoclosure @escaping. The annotations @noescape and @autoclosure(escaping) are deprecated. (SE-0103)

...

New in Xcode 8 beta – Swift and Apple LLVM Compilers: Swift Language

The @noescape and @autoclosure attributes must now be written before the parameter type instead of before the parameter name. [SE-0049]

Hence, you use the non-default @escaping attribute as follows; applied to the type of the closure parameter, rather than the parameter itself

func doSomething(withParameter parameter: Int, completion: @escaping () -> ()) {
    // ...
}

(Including my answer to a question in an upvoted comment below, as comments are not persistent data on SO)

@Cristi Băluță: "What does escaping do? Never seen this keywords before swift3 auto-conversion ... "

See e.g. the link to the SE-0103 evolution proposal above (as well as the quoted text from the beta 6 release notes): previously, closure parameters were escaping by default (hence no need for the existence of an explicit annotation for escaping), but are now instead non-escaping, by default. Hence the addition of @escaping to explicitly annotate that a closure parameter may escape (contrary to its default behaviour). This also explains why @noescape is now deprecated (no need to annotate the default behaviour).

For explaining what it means that a closure parameter is escaping, I quote the Language Reference - attributes:

"Apply this attribute to a parameter’s type in a method or function declaration to indicate that the parameter’s value can be stored for later execution. This means that the value is allowed to outlive the lifetime of the call."

查看更多
虎瘦雄心在
3楼-- · 2019-01-08 08:11

@noescape

From xcode 8 beta 6 @noescape is the default. Prior to that, @escaping was the default. Anybody updating to swift 3.0 from previous versions might face this error.

You can not store a @noescape closure inside a variable. Because if you can store a closure inside a variable, you can execute the closure from anywhere in your code. But @noescape states that the closure parameter can not escape the body of the function.

This will give compiler error in Xcode 8

class MyClass {

    var myClosure: (() -> ())?

    func doSomething(finishBlock: () -> ()) {
        myClosure = finishBlock    // ‼️ Error: Assigning non-escaping parameter 'finishBlock' to an @escaping closure
    }
}

This will compile ok (explicitly write @escaping)

class MyClass {

    var myClosure: (() -> ())?

    func doSomething(finishBlock: @escaping () -> ()) {
        myClosure = finishBlock
    }
}

Benefits of @noescape:

  • Compiler can optimize your code for better Performance
  • Compiler can take care of memory management
  • There is no need to use a weak reference to self in the closure


For details check out: Make non-escaping closures the default

查看更多
登录 后发表回答