Swift 3可选的转义闭包参数

时间:2016-09-21 14:01:53

标签: function closures swift3 optional

假设:

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()

有没有办法制作completion action类型参数(和Action?)并保留@escaping

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

error: @escaping attribute only applies to function types

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

5 个答案:

答案 0 :(得分:180)

来自:swift-users mailing list

  

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

因此默认情况下,可选的函数参数是@escaping @noeascape默认仅适用于函数参数。

答案 1 :(得分:100)

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尚未解决。修复那个bug,引入了一个新的(你正面临的那个)仍然是开放的。见SR-2444

解决方法 @Michael Ilseman 指出作为临时解决方案是从可选功能类型中移除@escaping属性,使函数保持转义

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

编辑2:

SR-2444已被关闭,明确说明参数位置中的闭包不是转义,需要用@escaping标记它们以使它们转义,但可选参数 隐式转义,因为((Int)->())?Optional<(Int)->()>的同义词,可选闭包正在转义。

答案 2 :(得分:15)

我遇到类似的问题,因为混合@escaping和非@escaping非常混乱,特别是如果你需要传递闭包。我最终得到了默认参数(我认为这更有意义)

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)
}

答案 3 :(得分:15)

我只是通过这种方式在没有任何警告的情况下在Swift 3中工作:

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

答案 4 :(得分:1)

该示例中要理解的重要一点是,如果将testImageBuild更改为Action,闭包将会转义。所以,让我们按照您的建议进行操作:

Action?

好的,现在我们叫typealias Action = () -> () var action: Action? = { } func doStuff(stuff: String, completion: Action?) { print(stuff) action = completion completion?() }

doStuff

好吧,该要求仅适用于转义闭包。所以关闭是逃避。这就是为什么您不标记转义-它已经转义了。