繁体   English   中英

Swift 可选的转义闭包参数

[英]Swift optional escaping closure parameter

鉴于:

typealias Action = () -> ()

var action: Action = { }

func doStuff(stuff: String, completion: @escaping Action) {
    print(stuff)
    action = completion
    completion()
}

func doStuffAgain() {
    print("again")
    action()
}

doStuff(stuff: "do stuff") { 
    print("swift 3!")
}

doStuffAgain()

有什么方法可以制作Action?类型的completion参数(和actionAction? 并保持@escaping

更改类型会出现以下错误:

@escaping 属性仅适用于函数类型

删除@escaping属性,代码编译并运行,但似乎不正确,因为completion闭包正在转义函数的范围。

来自: swift-users 邮件列表

基本上,@escaping 仅对函数参数位置的闭包有效。 noescape-by-default 规则仅适用于函数参数位置的这些闭包,否则它们将被转义。 聚合体,例如带有关联值的枚举(例如 Optional)、元组、结构体等,如果它们有闭包,那么对于不在函数参数位置的闭包,即它们是转义的,遵循默认规则。

所以可选的函数参数默认是@escaping。
@noeascape 默认仅适用于函数参数。

有一个SR-2552报告说@escaping无法识别函数类型别名。 这就是错误@escaping attribute only applies to function types 您可以通过扩展函数签名中的函数类型来解决:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
    print(stuff)
    action = completion
    completion?()
}

func doStuffAgain() {
    print("again")
    action?()
}

doStuff(stuff: "do stuff") {
    print("swift 3!")
}

doStuffAgain()

编辑 1: :

我实际上是在 xcode 8 beta 版本下,其中错误SR-2552尚未解决。 修复该错误,引入了一个仍处于打开状态的新错误(您面临的错误)。 SR-2444

@Michael Ilseman指出的临时解决方法是从可选函数类型中删除@escaping属性,使函数保持为 escaping

func doStuff(stuff: String, completion: Action?) {...}

编辑2 ::

SR-2444已经被关闭,说明明确,在参数关闭位置逃逸,需要他们被打上@escaping让他们逃脱,但可选参数隐含逃脱,因为((Int)->())? Optional<(Int)->()>的同义词,可选闭包正在转义。

我遇到了类似的问题,因为混合@escaping和非@escaping非常令人困惑,特别是如果您需要传递闭包。

我最终通过= { _ in }为闭包参数分配了一个无操作默认值,我认为这更有意义:

func doStuff(stuff: String = "do stuff",
        completion: @escaping (_ some: String) -> Void = { _ in }) {
     completion(stuff)
}

doStuff(stuff: "bla") {
    stuff in
    print(stuff)
}

doStuff() {
    stuff in
    print(stuff)
}

我让它在 Swift 3 中工作,没有任何警告,只有这样:

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}

在示例中要理解的重要一点是,如果将Action更改为Action? 关闭正在逃脱。 所以,让我们按照你的建议去做:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: Action?) {
    print(stuff)
    action = completion
    completion?()
}

好的,现在我们将调用doStuff

class ViewController: UIViewController {
    var prop = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        doStuff(stuff: "do stuff") {
            print("swift 3!")
            print(prop) // error: Reference to property 'prop' in closure 
                        // requires explicit 'self.' to make capture semantics explicit
        }
    }
}

嗯,这个要求只出现在转义闭包中。 所以关闭正在逃逸。 这就是为什么你没有标记为转义 - 它已经在转义了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM