Swift 3可选的转义闭包参数

Les*_*nel 148 closures function optional swift3

鉴于:

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()
Run Code Online (Sandbox Code Playgroud)

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

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

error: @escaping attribute only applies to function types

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

Dmi*_*rov 197

来自:swift-users邮件列表

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

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

  • 我认为这会给主题增加最重要的信息,应该接受答案. (5认同)
  • 这是有道理的,因为技术上说 `(()->Void)?` 与说你有 `Optional<()->Void>` 是一样的,为了让 `Optional` 保持所有权,它只需要接受`@escaping` 函数。我认为这应该是公认的答案。谢谢你。 (3认同)

Jan*_*ans 110

有一个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()
Run Code Online (Sandbox Code Playgroud)

编辑1 ::

我实际上是在xcode 8 beta版本下,其中错误SR-2552尚未解决.修复那个bug,引入了一个仍然开放的新bug(你正面临的那个).见SR-2444.

解决方法@Michael Ilseman指出,临时解决方案是@escaping从可选函数类型中删除属性,使函数保持转义状态.

func doStuff(stuff: String, completion: Action?) {...}
Run Code Online (Sandbox Code Playgroud)

编辑2 ::

SR-2444已经被关闭,说明明确,在参数位置关闭逃逸,需要他们与被标记@escaping,使他们逃脱,但可选参数隐含逃避,因为((Int)->())?是同义词Optional<(Int)->()>,可选的闭包逃跑.

  • 在进一步阅读后,我看到SR-2444表示所有可选的闭包被视为转义,这是一个补充的错误:)我将假设当它被修复时,编译将警告我们进行更改. (47认同)
  • 现在得到`@escaping可能只适用于函数类型func doStuff的参数(stuff:String,completion:(@ escaping() - >())?){` (5认同)

小智 17

我遇到类似的问题,因为混合@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)
}
Run Code Online (Sandbox Code Playgroud)

  • 如果我想检查这个块是否为零/空怎么办? (4认同)
  • 无赖,这不适用于协议方法。“协议方法中不允许使用默认参数”(Xcode 8.3.2)。 (2认同)

Igo*_*gor 17

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

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}
Run Code Online (Sandbox Code Playgroud)


mat*_*att 5

在示例中要理解的重要一点是,如果更改ActionAction?闭包,则会转义。那么,让我们按照您的建议进行:

typealias Action = () -> ()

var action: Action? = { }

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

Run Code Online (Sandbox Code Playgroud)

好的,现在我们将调用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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

嗯,这个要求只在逃避闭包时出现。所以闭包正在逃逸。这就是为什么你不将它标记为转义——它已经在转义了。