bei*_*ian 18 closures swift swift3
我注意到在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
}
}
}
Run Code Online (Sandbox Code Playgroud)
Ham*_*ish 12
在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)
}
}
}
Run Code Online (Sandbox Code Playgroud)
如closure在上面的例子是非转义,它是从被存储或捕获的,因而限制了其使用寿命的功能的寿命禁止foo(_:).因此,这意味着它捕获的任何值都保证在函数退出后不会被捕获 - 这意味着您不必担心捕获时可能出现的问题,例如保留周期.
但是,闭包存储属性(例如bar在上面的例子中)是按照定义转义(用它来标记它@noescape是没有意义的),因为它的生命周期不限于给定的函数 - 它(因此它所有捕获的变量)将保留在只要给定实例保留在内存中,就可以使用内存.因此,这很容易导致诸如保留周期之类的问题,这就是为什么需要使用显式self.来使捕获语义显式化的原因.
事实上,例如,您的示例代码将在viewDidLoad()被调用时创建一个保留周期,someClosure强烈捕获self和self强引用someClosure,因为它是一个存储属性.
值得注意的是,作为"存储函数属性始终转义"规则的扩展,存储在聚合中的函数(即具有关联值的结构和枚举)也总是转义,因为对此类聚合的处理没有限制.正如pandaren codemaster所指出的,这当前包括Optional- 意味着Optional<() -> Void>(aka.(() -> Void)?)总是逃避.编译器最终可能会将此作为函数参数的一个特例,因为可选的已构建在很多编译器魔法上.
当然,您希望能够使用该@noescape属性的一个地方是一个闭包,它是函数中的局部变量.这样的闭包将具有可预测的寿命,只要它不存储在函数外部或被捕获.例如:
class A {
let n = 5
func foo() {
let f : @noescape () -> Void = {
print(n)
}
f()
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,正如@noescape在Swift 3中删除的那样,这是不可能的(有趣的是,在Xcode 8 GM中,它是可能的,但会产生弃用警告).正如Jon Shier所说,我们将不得不等待它重新添加到语言中,这可能会也可能不会发生.
| 归档时间: |
|
| 查看次数: |
3293 次 |
| 最近记录: |