将闭包更新为Swift 3-@escaping


75

我已经将代码更新为Xcode 8.0 beta 6,但是我对新的不可转义的闭包默认值感到困惑。在下面的代码的Xcode提示添加@escaping在前面的completion:在下面的第一行代码,但仍然无法编译,去的圈子。*

编辑:实际上,正如Xcode所建议的那样,@escaping应该添加在after之后 completion:。警报可能仍会显示,但清理和编译会将其删除。)*应该如何重新编写/修复此代码才能在更新的Swift 3中工作?我看过新手册,但找不到合适的代码示例。

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

    callSomeOtherFunc(withCompletion: completion)
  }

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

任何帮助,不胜感激!

Answers:


58

Swift 3:闭包参数属性现在应用于参数类型,而不是参数本身

在Swift 3之前,闭包属性@autoclosure@noescape曾经是闭包参数的属性,但现在是参数类型的属性;请参阅以下接受的Swift进化建议:

您的特定问题与参数类型属性@escaping(适用相同的新规则)有关,如已接受的Swift进化建议中所述,默认情况下让闭包参数不转义:

这些建议现在都在Xcode 8的beta阶段中实现(请参阅Xcode 8 beta 6的发行说明;访问需要开发者帐户登录)

Xcode 8 beta 6中的新增功能-Swift编译器:Swift语言

默认情况下,闭包参数是不转义的,而不是用显式注释@noescape。使用@escaping以指示关闭参数可以逃脱。@autoclosure(escaping)现在写为 @autoclosure @escaping。注释@noescape@autoclosure(escaping)已弃用。(SE-0103)

...

Xcode 8 beta中的新功能– Swift和Apple LLVM编译器:Swift语言

@noescape@autoclosure属性现在必须在参数名前的参数类型前,而不是被写入。[SE-0049]

因此,您可以@escaping按如下方式使用非默认属性:应用于闭包参数的类型,而不是参数本身

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

(因为我的评论不是关于SO的持久数据,所以在下面的评论中包括我对问题的回答)

@Cristi Băluță:“转义是做什么的?在swift3自动转换之前从未见过此关键字……”

参见例如上述SE-0103改进建议的链接(以及beta 6发行说明中引用的文本):以前,默认情况下转义了闭包参数(因此,无需存在用于转义的显式注释),但默认情况下现在改为不转义。因此,添加@escaping来显式地注释一个闭包参数可以转义(与其默认行为相反)。这也解释了为什么@noescape现在不推荐使用(无需注释默认行为)。

为了解释闭包参数转义的含义,我引用了语言参考-attribute

“将此属性应用于方法或函数声明中的参数类型,以指示该参数的值可以存储以供以后执行。这意味着该值可以超过调用的生命周期。”


感谢您对dfri的详尽回答。实际上,我确实在参数之前的正确位置添加了@转义,我只是注意到我的错误在说明中指出了这一点。Xcode仍然会像我描述的那样抱怨,但是无论如何进行清理和编译最终都会删除警报。
nontomatic

@nontomatic很乐意提供帮助。
dfrib '16

7
转义是做什么的?在swift3自动转换之前从未见过此关键字,我不认为我有任何损失。
Cristi Băluță

@ CristiBăluță例如,参见上述SE-0103升级建议的链接(以及beta 6发行说明中引用的文字):以前,闭包参数默认情况下是转义的(因此不需要注释,它们在转义),但现在却转义了。因此,添加@escaping来显式注释close参数可能会转义(与其默认行为相反)。这也解释了为什么@noescape不弃用(无需注释默认行为)的原因。
dfrib

3
@ SagarR.Kothari问题和答案均基于以下事实:我们知道转义和不转义闭包之间的区别,因此我先前对CristiBăluță的评论(回答了为什么现在存在此关键字)。为了解释它的作用,我引用了lang。引用:“将此属性应用于方法或函数声明中的参数类型,以指示该参数的值可以存储以供以后执行。这意味着该值可以保留调用的生命周期。”
dfrib

23

@noescape

从xcode 8 beta 6开始@noescape是默认的。在此之前,@escaping是默认设置。从以前的版本更新到swift 3.0的任何人都可能会遇到此错误。

您不能将@noescape闭包存储在变量内。因为如果可以将闭包存储在变量中,则可以在代码中的任何位置执行闭包。但是@noescape指出,闭包参数无法转义函数的主体。

这将在Xcode 8中产生编译器错误

class MyClass {

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

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

这将编译确定(明确地写@escaping

class MyClass {

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

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

好处@noescape

  • 编译器可以优化您的代码以获得更好的性能
  • 编译器可以负责内存管理
  • 闭包中无需使用对自身的弱引用


有关详细信息,请检查:将非转义的闭包设置为默认值

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.