我正在测试闭包中的保留周期,如下所示
class Sample {
deinit {
print("Destroying Sample")
}
func additionOf(a: Int, b:Int) {
print("Addition is \(a + b)")
}
func closure() {
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
self?.additionOf(3, b: 3)
usleep(500)
self?.additionOf(3, b: 5)
}
}
}
后来某个时候,我在做
var sample : Sample? = Sample()
sample?.closure()
dispatch_async(dispatch_get_global_queue(0, 0)) {
usleep(100)
sample = nil
}
输出将为
Addition is 6
Destroying Sample
这是可以理解的,因为self
在执行self?.additionOf(3, b:5)
之前为空
如果我在闭包内部做了一个修改,创建了另一个引用[weak self]
的变量,如下所示
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.additionOf(3, b: 3)
usleep(500)
strongSelf.additionOf(3, b: 5)
}
这一次,输出为
Addition is 6
Addition is 8
Destroying C
我的问题是,为什么strongSelf
在sample = nil
之后不为空,是因为它在sample = nil
之前被捕获到闭包中吗
1条答案
按热度按时间enyaitl31#
让我们考虑一下与你的第二个例子相对应的当代例子:
这是一种常见的模式,它实际上表示“如果
self
已被释放,则立即释放return
,否则建立对self
的强引用,并维护此强引用直到闭包完成”。所以,在你的例子中,如果
self
不是nil
当这个被分派的块开始时,那么一旦我们点击guard
语句,我们现在就有了两个对这个Sample
对象的强引用,原始的sample
引用和这个闭包中的这个新引用。因此,当其他线程移除它自己对
Sample
对象的强引用sample
时(通过超出作用域或显式将sample
设置为nil
),该闭包的强引用仍然存在,并将阻止该对象被释放(或至少直到该调度块完成)。在你上面的评论中,你问:
我仍然不明白为什么
strongSelf
没有得到改变,因为自我是nil
了...强引用永远不会仅仅因为其他强引用(即使它是原始的强引用)被设置为
nil
而被设置为nil
。引用被设置为nil
的行为只适用于weak
引用,而不适用于强引用。当你的代码在第一个线程上执行
sample = nil
时,它所做的只是移除一个强引用,而不是删除对象或类似的东西,它只是移除它的强引用,现在被调度的块有了自己的强引用,在sample = nil
上发生的所有事情是,具有两个强引用的对象现在只剩下一个强引用(在调度块中对它的引用)。只有当这个最后的强引用被移除时,对象才会被释放,并且deinit
将被调用。FWIW,如果您要立即调度到全局队列,
[weak self]
捕获列表实际上并没有太大的实用性。您可能会删除它,然后执行以下操作:[weak self]
捕获列表在闭包运行于某个稍后的时间点时最有用,例如,完成处理程序或asyncAfter
,其中self
有可能在闭包运行之前超出范围: