Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retain Cycles: Why is that such a bad thing?

There are two Objects A and B. A creates B and retains it. B has an instance variable that points to A, retaining it. So both retain eachother. Some people say, that this strong connection can't be broken ever again.

But is that really the case?

If B would release A, then A could easily release B, and so B would be deallocated. A would be deallocated as soon as it's other owner (I guess there must be someone) releases it.

Or does this problem only apply in a case where A does not create B, but just holds a strong reference to it through retaining it in an instance variable? I still don't see why that connection could not be broken up again.

like image 660
Thanks Avatar asked Apr 26 '09 18:04

Thanks


3 Answers

Cycles aren't bad, but they are often avoided because they can make it tricky to ensure you haven't got memory leaks. Leaks occur especially when objects are 'reference counted'. In a language or system that uses reference counting, an object keeps track of the number of references pointing at it. Every time a reference is deleted, the count goes down, when the count gets to zero, there are no references and so the object can be deleted.

This usually takes care of itself and works ok without any careful thinking. If you've got a group of objects with no cycles and you drop your reference to the root object, then it will be deleted, this means references it has to objects it owns will be dropped, the objects being referenced will have their reference counts go to zero. They'll be deleted and the cascade will cause all objects to be deleted.

But... if you have a cycle, this cascade doesn't work. You may have a group of objects and you don't want them any more, so you drop the only reference you have to these objects, but because there is a cycle the objects reference each other. This means their reference counts never go to zero, and they don't get deleted. This is a memory leak.

Clearly, you can do some careful management and break the cycles before you drop your reference to a group of objects you don't want any more. But... as I just said, this takes careful management. It's very easy to get wrong. This is one of the main reasons that memory leaks occur.

To avoid the risk of leaks and the tricky job of breaking cycles correctly when you no longer need a group of objects, programmers usually try to avoid cycles. This becomes more important on big projects with many programmers where no one person understands the whole system. If there were cycles, the programmers would have to watch out and spend a long time studying each others code to avoid cycles.

Some languages with garbage collectors (eg C#) can delete a group of objects that are no longer needed even if the group contains cycles.

like image 145
Scott Langham Avatar answered Oct 10 '22 01:10

Scott Langham


A retain cycle can be broken, if you know about it. Usually it leads to nasty bugs (memory leaks). In your example:

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

Now, a unnamed B instance (created by A) has a retain count of 1. Since we hold a reference to A and the anonymous B instance holds a strong reference to A, A's retain count is 2.

Let's say, we are done using A:

[a release];
return 12;

Now, A's retain count is 1. It will not be released, it's memory is lost. That's why retain cycles are bad.

like image 7
rincewind Avatar answered Oct 10 '22 02:10

rincewind


The way to break a retain loop is to have a separate "close" method.

i.e.

A retains B
B retains A

when you're done, call a method (I'll call it "close") on A where A releases B. You can then release A and the whole loop will release (assuming there are no retains elsewhere).

like image 3
Matt Gallagher Avatar answered Oct 10 '22 00:10

Matt Gallagher