将闭包更新到 Swift 3 - @escaping

Posted

技术标签:

【中文标题】将闭包更新到 Swift 3 - @escaping【英文标题】:Updating closures to Swift 3 - @escaping 【发布时间】:2016-12-28 01:24:13 【问题描述】:

我已将我的代码更新到 Xcode 8.0 beta 6,但我被新的非转义闭包默认值所困扰。在下面的代码中,Xcode 建议在下面代码的第一行的completion: 前面添加@escaping,但这仍然无法编译并循环往复。 *

(EDIT:事实上,@escaping 应该添加在 after completion: 中,正如 Xcode 所建议的那样。警报可能仍会显示,但清理和编译会删除它.)* 应该如何重写/修复此代码以在更新的 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

非常感谢任何帮助!

【问题讨论】:

【参考方案1】:

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

在 Swift 3 之前,闭包属性 @autoclosure@noescape 曾经是闭包参数的属性,但现在是参数type的属性;请参阅以下已接受的 Swift 进化提案:

SE-0049: Move @noescape and @autoclosure to be type attributes

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

SE-0103: Make non-escaping closures the default

这些提案现在都在 Xcode 8 的 beta 阶段实现(参见 release notes for 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 () -> ()) 
    // ...


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

@Cristi Băluță:“转义有什么作用?从未见过这个关键字 swift3自动转换前..."

参见例如SE-0103 evolution proposal above 的链接(以及 beta 6 发行说明中引用的文本):以前,闭包参数默认转义(因此不需要存在用于转义的显式注释),但现在不是- 转义,默认情况下。因此添加了@escaping 以明确注释闭包参数可能会逃逸(与其默认行为相反)。这也解释了为什么 @noescape 现在已被弃用(无需注释默认行为)。

为了解释闭包参数转义的含义,我引用Language Reference - attributes:

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

【讨论】:

感谢 dfri 的彻底回答。我实际上确实在参数之前的正确位置添加了@escaping,我只是注意到我的错误在描述中指出了这一点。 Xcode 仍然会像我描述的那样抱怨,但是无论如何进行清理和编译最终都会删除警报。 @nontomatic 很乐意为您提供帮助。 转义有什么作用?在 swift3 自动转换之前从未见过这个关键字,我认为我没有丢失任何东西。 @CristiBăluță 参见例如上面SE-0103 evolution proposal的链接(以及beta 6发行说明中引用的文本):以前,闭包参数默认转义(因此不需要注释它们正在转义),但现在不是逃脱。因此添加了@escaping 以明确注释关闭参数可能会逃逸(与其默认行为相反)。这也解释了为什么 @noescape 未被弃用(无需注释默认行为)。 @SagarR.Kothari 问题和答案都基于我们知道转义和非转义闭包之间的区别这一事实,因此我之前对 CristiBăluță 的评论(回答 为什么这个关键字现在存在)。为了解释它的作用,我引用了 lang。 ref.: "将此属性应用于方法或函数声明中的参数类型,以指示可以存储参数的值以供以后执行。这意味着允许该值超过调用的生命周期。".【参考方案2】:

@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的好处:

编译器可以优化您的代码以获得更好的性能 编译器可以处理内存管理 没有必要在闭包中使用对 self 的弱引用

详情请查看:Make non-escaping closures the default

【讨论】:

以上是关于将闭包更新到 Swift 3 - @escaping的主要内容,如果未能解决你的问题,请参考以下文章

swift 将闭包映射到swift中的数组

为啥 Swift 不更新闭包外的变量?

Swift-使用完成处理程序更新闭包外的全局变量

无法在 Swift 3 中使用闭包语法

将 Swift 闭包转换为 CFunctionPointer?

swift中的闭包总结