有两个对象A和B.A创建B并保留它。 B有一个指向A的实例变量,保留它。所以两者都保持着彼此。有人说,这种强大的联系不能再被打破。
但情况确实如此吗?
如果B会释放A,那么A可以很容易地释放B,因此B将被解除分配。一旦它的其他所有者(我想必定有人)释放它,它就会被解除分配。
或者此问题仅适用于A不创建B的情况,但只是通过将其保留在实例变量中来保存对它的强引用?我仍然不明白为什么这种联系不能再被打破。
答案 0 :(得分:15)
循环并不坏,但它们经常被避免,因为它们可以使它变得棘手,以确保您没有内存泄漏。特别是当对象被“引用计数”时会发生泄漏。在使用引用计数的语言或系统中,对象会跟踪指向它的引用数。每次删除引用时,计数都会下降,当计数变为零时,没有引用,因此可以删除该对象。
这通常会照顾好自己,并且无需仔细考虑即可正常工作。如果你有一组没有循环的对象,你删除了对根对象的引用,那么它将被删除,这意味着它对它拥有的对象的引用将被删除,被引用的对象将具有它们的引用计数归零。它们将被删除,级联将导致所有对象被删除。
但是......如果你有一个循环,这个级联不起作用。您可能有一组对象,并且您不再需要它们,因此您只删除对这些对象的唯一引用,但因为有一个循环,所以对象相互引用。这意味着它们的引用计数永远不会为零,并且它们不会被删除。这是内存泄漏。
显然,在放弃对一组不再需要的对象的引用之前,您可以进行一些仔细的管理并打破周期。但是......正如我刚才所说,这需要谨慎管理。这很容易出错。这是发生内存泄漏的主要原因之一。
为了避免泄漏的风险以及在不再需要一组对象时正确打破周期的棘手工作,程序员通常会尝试避免循环。对于那些没有人理解整个系统的程序员而言,这对于大型项目来说变得更加重要。如果有周期,程序员必须注意并花费很长时间研究彼此的代码以避免周期。
某些带有垃圾收集器的语言(例如C#)可以删除一组不再需要的对象,即使该组包含循环也是如此。
答案 1 :(得分:7)
如果您了解保留周期,可以打破保留周期。通常它会导致讨厌的错误(内存泄漏)。在您的示例中:
A* a = [[A alloc] initAndCreateB];
现在,一个未命名的B实例(由A创建)的保留计数为1.由于我们持有对A的引用而匿名B实例持有对A的强引用,因此A的保留计数为2.
让我们说,我们完成了使用A:
[a release];
return 12;
现在,A的保留计数为1.它不会被释放,它的内存会丢失。这就是保留周期不好的原因。
答案 2 :(得分:3)
打破保留循环的方法是使用单独的“关闭”方法。
即
A retains B
B retains A
当你完成后,在A上调用一个方法(我将其称为“close
”),其中A释放B.然后你可以释放A并且整个循环将释放(假设没有保留其它地方)。
答案 3 :(得分:2)
问题是:A指向并保留B,B指向并保留A.当没有其他A或B的引用时,将无法释放它们,因为您的应用程序没有那时对它们的任何引用。这称为参考周期,它是任何参考计数系统中常见的一种内存泄漏。在大多数高级语言中解决这个问题的方法是使用垃圾收集而不是引用计数。