可选的快速转义闭包参数

给出:

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

更改类型会产生以下错误:

@ escape 属性只适用于函数类型

删除 @escaping属性后,代码将编译并运行,但似乎不正确,因为 completion闭包逃脱了函数的作用域。

77466 次浏览

有一个 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 版本下,那里的 bug SR-2552还没有解决。修复了那个 bug,引入了一个新的 bug (你正面临的那个) ,它仍然处于开放状态。见 SR-2444

作为临时解决方案的解决办法是从可选函数类型 保持函数为逃逸中删除 @escaping属性。

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

编辑2:

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

译自: 美国《每日邮报》网站(http://lists.Swift. org/pipermail/swift-users/Week-of-Mon-20160905/003185.html)

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

所以默认情况下,可选的函数参数是@转义。
默认情况下,@noeascape 只应用于函数参数。

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

最后,我通过 = { _ in }为闭包参数分配了一个 no-op 默认值,我认为这样做更有意义:

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

这个要求只出现在转义闭包时。所以封闭逃脱了。这就是为什么你不 Mark它逃跑-它已经逃跑了。