为什么闭包在 Swift 3 中默认情况下都是非转义的,为什么需要显式的“self"? [英] Why do closures require an explicit `self` when they're all non-escaping by default in Swift 3?

查看:20
本文介绍了为什么闭包在 Swift 3 中默认情况下都是非转义的,为什么需要显式的“self"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've noticed that in Swift 2.2, closures marked as non-escaping with @noescape do not require an explicit self. In Swift 3, all closures are non-escaping by default and now requires them to be marked with @escaping if you want them be able to escape.

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

Given that all of the closures in Swift 3 by default are non-escaping, why do they require an explicit 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
        }
    }
}

推荐答案

在 Swift 3 中,所有闭包默认都是非转义的

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

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

No, in Swift 3, only closure function arguments (i.e function inputs that are functions themselves) are non-escaping by default (as per SE-0103). For example:

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(_:)的生​​命周期代码>.因此,这意味着它捕获的任何值都保证在函数退出后不会保持捕获状态——这意味着您无需担心捕获可能出现的问题,例如保留周期.

As closure in the above example is non-escaping, it is prohibited from being stored or captured, thus limiting its lifetime to the lifetime of the function foo(_:). This therefore means that any values it captures are guaranteed to not remain captured after the function exits – meaning that you don’t need to worry about problems that can occur with capturing, such as retain cycles.

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

However, a closure stored property (such as bar in the above example) is by definition escaping (it would be nonsensical to mark it with @noescape) as its lifetime not limited to a given function – it (and therefore all its captured variables) will remain in memory as long as the given instance remains in memory. This can therefore easily lead to problems such as retain cycles, which is why you need to use an explicit self. in order to make the capturing semantics explicit.

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

In fact, case in point, your example code will create a retain cycle upon viewDidLoad() being called, as someClosure strongly captures self, and self strongly references someClosure, as it's a stored property.

值得注意的是,作为存储的函数属性总是转义"规则的扩展,存储在聚合中的函数(即具有关联值的结构和枚举)也总是转义,因为对您使用这些属性执行的操作没有限制聚合.正如 指出的那样由熊猫人代码管理员出品,这个目前包括Optional——这意味着Optional<() ->Void> (aka. (() -> Void)?) 总是在逃避.不过,编译器最终可能会将其作为函数参数的一个特例,因为 optional 已经建立在许多编译器魔法之上.

It's worth noting that as an extension of the "stored function properties are always escaping" rule, functions stored in aggregates (i.e structures and enumerations with associated values) are also always escaping, as there's no restrictions on what you do with such aggregates. As pointed out by pandaren codemaster, this currently includes Optional – meaning that Optional<() -> Void> (aka. (() -> Void)?) is always escaping. The compiler might eventually make this a special case for function parameters though, given that optional is already built on a lot of compiler magic.

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

Of course, one place where you would expect to be able to use the @noescape attribute is on a closure that’s a local variable in a function. Such a closure would have a predictable lifetime, as long as it isn’t stored outside of the function, or captured. For example:

class A {

    let n = 5

    func foo() {

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

        f()
    }
}

不幸的是,由于 @noescape 在 Swift 3 中被删除,这是不可能的(有趣的是,在 Xcode 8 GM 中,这是可能的,但会产生弃用警告).正如 Jon Shier 所说,我们将不得不等待它被重新添加到语言中,这可能或可能不会发生.

Unfortunately, as @noescape is being removed in Swift 3, this won't be possible (What's interesting is that in Xcode 8 GM, it is possible, but yields a deprecation warning). As Jon Shier says, we’ll have to wait for it to be re-added to the language, which may or may not happen.

这篇关于为什么闭包在 Swift 3 中默认情况下都是非转义的,为什么需要显式的“self"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆