在阅读了一些苹果的文章和开发人员指南后,我仍然对关闭中的捕获列表感到困惑。 "捕获"是什么意思,它在幕后如何无主自我和弱自我发挥作用?闭包如何在不拥有对象的情况下使用自我? 我认为这就像制作该对象的副本,因此当它完成时,它会像值类型一样从堆栈传递,但我想我错了。 我希望这里有人可以更容易和更清晰地理解,或者将我链接到一篇回答这个特定问题的好文章。 感谢您的提前
我的理解,可能有点简化,是关于所有权和持有一个对象,这意味着只要我们声称一个对象的所有权,它就不能从内存中释放,即使代码的另一部分将其设置为 nil 或类似。
有了weak
,我们说可以销毁该物体,并且只有在它仍然存在时才使用它。
因此,当在闭包中将self
声明为weak
时,我们说如果self
在执行闭包时仍然存在,我们通常会这样做,否则闭包将被静默忽略而不会产生错误。
这主要与引用计数有关。在闭包内部使用(但在闭包外部声明(的任何实例都会被强引用(即其引用计数递增(。这可能导致保留周期,例如
class MyClass {
var myClosure: (() -> Void)!
init() {
myClosure = {
self.foo()
}
}
func foo() {
}
}
在上面的示例中,MyClass
实例保留对myClosure
的引用,反之亦然,这意味着MyClass
实例将永远保留在内存中。
你也可以有更复杂/更难发现的保留周期,所以你需要真正注意,如果你有任何疑问,在你的类deinit
方法中添加一些print
调用,只是为了确保(或使用 Instruments(。
为了避免这些问题,您可以将闭包中捕获的对象标记为unowned
或weak
。这意味着它们的引用计数不会增加,您可以避免这些保留周期。上面的例子可以这样完成:
myClosure = { [weak self] in
self?.foo()
}
或者,对于此示例来说,更好的是这样:
myClosure = { [unowned self] in
self.foo()
}
虽然第一种方法始终是安全的,并且您更有可能执行的操作,但在此示例中,unowned
版本很容易推理,因为您知道myClosure
不会超过self
。但是,如果您不是 100% 确定self
将始终比关闭使用weak
寿命。
另请注意,您可以标记如何捕获闭包中使用的多个对象,只需用逗号分隔即可,例如
myClosure = { [weak self, unowned bar] in
self?.foo(bar)
}
如果我们记住捕获的值默认是闭包中的强引用,我们可以假设这可以创建保留周期(坏东西(。
捕获列表是可以传递到闭包中的变量数组。捕获列表的目的是更改传入的变量的强度。这用于中断保留周期。
例如:
// strong reference
[label = self.myLabel!] in
// weak reference
[weak label = self.myLabel!] in
// unowned reference
[unowned self] in