views:

432

answers:

4

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.

+2  A: 

The problem is this: A points to and retains B, and B points to and retains A. When there are no other references to A or B, there will be no way to release them, because you app doesn't have any references to them at that point. This is called a reference cycle, and it a type of memory leak common in any reference counted system. The way this is solved in most high level languages is by using garbage collection rather than reference counting.

Zifre
Thanks. But why should there be no other reference to A or B? In which case could I have two objects where my App has no reference to them?
Thanks
If you do have other references to A or B, there is no problem. It is only when you lose those references (like if they go out scope) that it is a problem.
Zifre
+7  A: 

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 there 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.

Scott Langham
Objective-C's garbage collector (when enabled) can also delete retain-loop groups (almost any garbage collector can) but this is not relevant on the iPhone, where Objective-C garbage collection is not supported.
Matt Gallagher
+2  A: 

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.

rincewind
Thanks. For my understanding it doesn't look like if there are two different objects. A must be an Superclass of B, so that B's data structure can fit into A. But then after doing that, wouldn't a just be the anonymous B? I might be wrong. It's late ;)
Thanks
A does not have to be a superclass of B. It merely has an ivar that points to a B instance. The B is a separate object. Think about classes you see every day: You might have an NSArray, and NSString and an NSDate as instance variables. Those are *not* subclasses of your custom class. They're independent classes that yours makes use of.
Chuck
+2  A: 

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).

Matt Gallagher