在阅读了一些苹果的文章和开发者指南之后,我仍然对关闭捕获列表感到困惑。“捕获”是什么意思,在无主自我和弱自我方面如何在幕后工作?闭包如何在不拥有对象的情况下使用 self?我认为它就像制作该对象的副本,所以当它完成时,它会像值类型一样从堆栈传递,但我想我错了。我希望这里的某人可以使它更容易理解,或者将我链接到回答这个特定问题的好文章。感谢提前
3 回答
它主要与引用计数有关。任何在闭包内部使用(但在外部声明)的实例都被强引用(即它的引用计数增加)。这可能导致保留周期,例如
class MyClass {
var myClosure: (() -> Void)!
init() {
myClosure = {
self.foo()
}
}
func foo() {
}
}
在上面的例子中,实例MyClass
保留了对的引用,myClosure
反之亦然,这意味着MyClass
实例将永远留在内存中。
您还可以有更复杂/更难发现的保留周期,因此您需要真正注意,如果您有任何疑问print
,请在您的类方法中添加一些调用,deinit
以确保(或使用仪器)。
为了避免这些问题,您可以将在闭包中捕获的对象标记为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)
}
我的理解(可能有点简化)是关于所有权和对对象的持有,这意味着只要我们声明对象的所有权,它就不能从内存中释放,即使代码的另一部分设置它为零或类似。
weak
我们说可以销毁对象,并且我们只会在它仍然存在的情况下使用它。
因此,当在闭包中声明时self
,weak
我们说如果self
在执行闭包的时候仍然存在,我们通常会这样做,否则闭包将被默默地忽略而不会产生错误。
如果我们记住默认情况下捕获的值是闭包中的强引用,我们可以假设这会创建保留循环(坏东西)。
捕获列表是可以传递给闭包的变量数组。捕获列表的目的是改变传入变量的强度。这用于打破保留循环。
例如:
// strong reference
[label = self.myLabel!] in
// weak reference
[weak label = self.myLabel!] in
// unowned reference
[unowned self] in