我注意到在Swift 2.2中,标为non-escaped的闭包@noescape不需要explicit self。在Swift 3中,默认情况下,所有闭包都是不转义的,@escaping如果您希望它们能够转义,现在要求将其标记出来。
@noescape
self
@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 } } }
在Swift 3中,默认情况下所有闭包都不转义
不,在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(_:)。因此,这意味着保证它捕获的任何值在函数退出后都不会保留—这意味着您不必担心捕获可能会发生的问题,例如保留周期。
closure
foo(_:)
然而,一个闭合件 存储的属性 (例如,bar在上面的例子)是由定义逸出(这将是无意义的与将其标记@noescape)作为它的寿命 不 限定于给定功能的-它(以及因此它的所有捕获的变量)将保持在只要给定实例保留在内存中即可。因此,这很容易导致诸如保留周期之类的问题,这就是为什么您需要使用一个显式self.的以便使捕获语义显式的原因。
bar
self.
实际上,举例来说,您的示例代码在viewDidLoad()被调用时会创建一个保留周期,因为它是一个存储属性,因此someClosure强烈地捕获self并self强烈引用someClosure了它。
viewDidLoad()
someClosure
值得注意的是,作为“存储的函数属性始终转义”规则的扩展,聚合中存储的函数(即具有关联值的结构和枚举)也总是转义,因为对此类聚合没有任何限制。正如pandaren codemaster指出的那样, 当前 包括Optional–意味着Optional<() -> Void>(aka。(() -> Void)?)总是在逃避。鉴于可选参数已经建立在许多编译器魔术上,因此编译器最终可能会将其作为函数参数的特殊情况。
Optional
Optional<() -> Void>
(() -> Void)?
当然,您希望能够使用该@noescape属性的一个地方是闭包,该闭包是函数中的局部变量。只要不将其存储在函数外部或未被捕获,此类闭合将具有可预测的寿命。例如:
class A { let n = 5 func foo() { let f : @noescape () -> Void = { print(n) } f() } }
不幸的@noescape是,这在Swift 3中已被删除,这是不可能的(有趣的是,在Xcode 8 GM中,这是可能的,但是会产生弃用警告)。正如JonShier所说,我们必须等待它重新添加到语言中,这可能会或可能不会发生。