swift - 为什么闭包在 Swift 3 中默认都是非转义的而需要显式的 `self`?

标签 swift swift3 closures

我注意到在 Swift 2.2 中,使用 @noescape 标记为非转义的闭包不需要显式的 self。在 Swift 3 中,默认情况下所有闭包都是非转义的,现在如果您希望它们能够转义,则需要使用 @escaping 标记它们。

鉴于默认情况下 Swift 3 中的所有闭包都是非转义的,为什么它们需要显式的 self

final class SomeViewController: NSViewController {

    var someClosure: () -> () = { _ in }

    override func viewDidLoad() {
        super.viewDidLoad()

        someClosure = {
            view.layer = CALayer() // ERROR: Implicit use of `self` in closure; use `self.` to make capture semantics explicit
        }
    }
}

最佳答案

In Swift 3, all closures are non-escaping by default

不,在 Swift 3 中,只有闭包函数参数(即本身就是函数的函数输入)在默认情况下是非转义的(根据 SE-0103 )。例如:

class A {

    let n = 5
    var bar : () -> Void = {}

    func foo(_ closure: () -> Void) {
        bar = closure // As closure is non-escaping, it is illegal to store it.
    }

    func baz() {
        foo {
            // no explict 'self.' required in order to capture n,
            // as foo's closure argument is non-escaping,
            // therefore n is guaranteed to only be captured for the lifetime of foo(_:)
            print(n)
        }
    }
}

作为closure在上面的例子中是非转义的,禁止被存储或捕获,从而将其生命周期限制为函数的生命周期 foo(_:) .因此,这意味着它捕获的任何值都保证在函数退出后不会保持捕获状态——这意味着您无需担心捕获时可能发生的问题,例如保留周期。

但是,闭包存储的属性(例如上例中的 bar)根据定义转义(用 @noescape 标记它是荒谬的)作为其生命周期 限于给定的函数——只要给定的实例保留在内存中,它(以及因此捕获的所有变量)就会保留在内存中。因此,这很容易导致诸如保留循环之类的问题,这就是为什么您需要使用显式 self. 的原因。为了使捕获语义明确。

事实上,举例来说,您的示例代码将在 viewDidLoad() 上创建一个保留周期。被称为someClosure强烈捕获 self , 和 self强烈引用someClosure ,因为它是一个存储属性。

值得注意的是,作为“存储函数属性总是转义”规则的扩展,存储在聚合中的函数(即具有关联值的结构和枚举)也总是转义,因为对您的操作没有任何限制聚合体。作为pointed out by pandaren codemaster ,这个当前包括Optional – 意思是 Optional<() -> Void> (又名 (() -> Void)? )总是在转义。不过,编译器最终可能会将其作为函数参数的特例,因为可选的已经建立在很多编译器的魔法之上。


当然,您希望能够使用 @noescape 的地方attribute 在闭包上,闭包是函数中的局部变量。这样的闭包将具有可预测的生命周期,只要它不存储在函数外部或被捕获。例如:

class A {

    let n = 5

    func foo() {

        let f : @noescape () -> Void = {
            print(n)
        }

        f()
    }
}

不幸的是,作为@noescape在 Swift 3 中被删除,这是不可能的(有趣的是,在 Xcode 8 GM 中,这是可能的,但会产生弃用警告)。作为Jon Shier says ,我们将不得不等待它被重新添加到语言中,这可能发生也可能不会发生。

关于swift - 为什么闭包在 Swift 3 中默认都是非转义的而需要显式的 `self`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39433221/

相关文章:

ios - 如何用单个表格部分显示不同的 View ?

ios - 手动安装 SwiftKeychainWrapper

javascript - 在函数中使用查询到的json数据

closures - 在创建对象的函数中引用 var

closures - 是否有一个 let/flet/标签之类的概念用于绑定(bind)闭包以避免 funcall?

ios - 在 Swift 中捕获闭包值

swift - Cocoa 监听键盘命令+向上事件

ios - 从标签复制按钮(IOS)

swift - 使用什么控件从相机捕获图像并在 ui 上显示为带有事件的缩略图 xamarin ios

ios - 遍历静态 TableView 中的所有标签