2009-04-26 86 views
10

有两个对象A和B.A创建B并保留它。 B有一个指向A的实例变量,保留它。所以两个都保留彼此。有人说,这种强大的联系不能再一次被打破。保留周期:为什么这么糟糕?

但是,真的是这样吗?

如果B会释放A,那么A可以轻松释放B,因此B将被释放。一旦它是其他所有者(我想必须有人)释放它,它就会被释放。

或者,这个问题只适用于A没有创建B的情况,但只是通过将它保留在实例变量中而强制引用它?我仍然不明白为什么这个连接不能再被打破。

回答

15

周期并不糟糕,但它们经常被避免,因为它们可以使得确保没有内存泄漏变得棘手。特别是当对象被“引用计数”时发生泄漏。在使用引用计数的语言或系统中,对象会跟踪指向它的引用数量。每次删除引用时,计数都会减少,当计数变为零时,就没有引用,因此可以删除该对象。

这通常照顾好自己,没有任何仔细的思考就可以正常工作。如果你有一组没有循环的对象,并且你删除了对根对象的引用,那么它将被删除,这意味着它必须将它所拥有的对象的引用放弃,被引用的对象将有它们的引用计数去零。他们将被删除,级联会导致所有对象被删除。

但是...如果你有一个周期,这个级联不起作用。你可能有一组对象,你不再需要它们,所以你放弃了对这些对象的唯一引用,但是因为有一个循环,对象互相引用。这意味着它们的引用计数永远不会变为零,并且它们不会被删除。这是内存泄漏。

很明显,你可以做一些仔细的管理和打破循环,然后再把你的引用放到一组你不想要的对象上。但是......就像我刚才所说的那样,这需要仔细的管理。错误很容易。这是发生内存泄漏的主要原因之一。

为了避免泄漏的风险以及当您不再需要一组对象时正确分解循环的棘手工作,程序员通常会尝试避免循环。对于许多程序员而言,对于那些没有人理解整个系统的大型项目而言,这变得更加重要。如果有周期,程序员必须小心并花费很长时间研究其他代码以避免周期。

使用垃圾收集器(例如C#)的某些语言可以删除一组不再需要的对象,即使该组包含循环。

+2

Objective-C的垃圾回收器(启用时)也可以删除retain-loop组(几乎所有垃圾回收器都可以),但这与iPhone中不相关,其中Objective-C垃圾回收不受支持。 – 2009-04-26 23:17:54

2

问题是这样的:A指向并保留B,B指向并保留A.当没有其他引用A或B时,将无法释放它们,因为您的应用程序不在这一点上有任何提及他们的信息。这被称为参考周期,它是任何参考计数系统中常见的一种内存泄漏。在大多数高级语言中解决这个问题的方式是使用垃圾收集而不是引用计数。

+0

谢谢。但为什么不应该有其他的A或B?在这种情况下,我可以有两个对象,我的应用程序没有提及它们? – Thanks 2009-04-26 18:58:20

+0

如果您对A或B有其他参考,则没有问题。只有当你失去这些引用(比如如果他们超出范围),这是一个问题。 – Zifre 2009-04-26 19:29:49

7

如果您了解它,可以打破保留周期。通常它会导致令人讨厌的错误(内存泄漏)。在你的例子中:

A* a = [[A alloc] initAndCreateB]; 

现在,一个未命名的B实例(由A创建)的保留计数为1。由于我们坚持A中的参考和匿名乙实例持有的强引用A,A的保留计数为2

比方说,我们使用的是做:

[a release]; 
return 12; 

现在,A的保留计数是1.它不会被释放,它的内存会丢失。这就是保留周期不好的原因。

3

打破保留循环的方法是有一个单独的“关闭”方法。

A retains B 
B retains A 

当你做,调用一个方法在A,其中A释放B.(我称之为“close”),则可以释放A和整个循环将发布(假设其他地方没有保留)。