5

ARC 是否保留对对象的无主引用计数?

那么,如果一个对象的强引用计数达到 0 并且该对象的无主引用计数 > 0,则该对象被取消初始化但没有被取消分配?只有当强引用计数和无主引用计数达到 0 时,它才会被解除分配?

我在一篇文章中读到过,我认为是在 Medium 上),但我不确定它是否正确。

4

1 回答 1

9

首先,让我们知道这些问题的答案都是我们通常应该避免依赖的实现细节。现在,开始回答:

ARC 是否保留对对象的无主引用计数?

是的,它是真实的。每个对象都有三个引用计数:强计数、无主计数和弱计数。

  • 强计数始终被存储(但存储时调整为 -1,因此存储的 0 表示强引用计数为 1,存储的 1 表示强引用计数为 2,依此类推)。

  • 无主计数也始终被存储,调整为 +1 表示所有强引用,并在反初始化结束时删除。

  • 弱引用计数仅在创建对象的第一个弱引用后存储。弱引用计数,如果存储的话,存储时加上+1的调整,代表所有无主引用,在对象被释放后被移除。

那么,如果一个对象的强引用计数达到 0 并且该对象的无主引用计数 > 0,则该对象被取消初始化但没有被取消分配?

正确的。对象被取消初始化:deinit对象的类和所有超类的 s 都运行,并且对象的任何本身是引用的属性都设置为 nil。但是,对象的内存不会被释放,因为对象的标头必须保持有效,直到对该unowned对象的最后一个引用被销毁。

只有当强引用计数和无主引用计数达到 0 时,它才会被解除分配?

正确的。当强引用计数和无主引用计数都达到零时,对象被释放。由于大多数对象从不被unowned引用引用,这通常是在最后一个强引用被销毁时。

你没有问弱引用,但为了完整起见,我也会解释一下。当一个对象被(或曾经)弱引用时,Swift 会为该对象分配它所谓的“side table entry”(或有时只是“side table”)。

  • 如果一个对象没有边表,那么强计数和无主计数直接存储在对象中,弱计数(必须为零)不存储。

  • 如果对象具有边表,则指向边表的指针存储在对象中。强计数、无主计数和弱计数以及返回对象的指针都存储在边表中。

对对象的弱引用存储为指向边表的指针,而不是指向对象的指针。这意味着即使仍然存在对它的弱引用,对象也可以被释放(不仅仅是取消初始化)。

如果没有对对象的弱引用,则在释放对象时释放边表。如果仍然存在弱引用,则对象被解除分配,但边表仍然被分配。当对已释放对象的最后一个弱引用被销毁时,将释放边表。

请注意,当 Swift 对象被取消初始化或解除分配时,弱引用不会立即设置为 nil(销毁)!仅当程序尝试加载引用或弱引用的容器被取消初始化时,对已取消初始化对象的弱引用才设置为 nil。(我所说的“容器”是指,例如,当一个对象具有weak var属性时。该对象是weak var引用的容器。)


RefCount.hSwift 源代码顶部的大注释解释了所有这些细节以及更多信息。


PS 还有另一种引用,unowned(unsafe)它不会调整任何引用计数。如果可能,您应该避免这种参考(并且几乎总是可以避免)。

于 2019-02-23T02:10:02.360 回答