Objective-C retain cycle between 2 objects

312 views Asked by At

Here is the code:

TestA *ta = [[TestA alloc] init];
TestB *tb = [[TestB alloc] init];  

ta.b = tb;
tb.a = ta;

I tried to set ta = nil or tb = nil. It didn't work but ta.b = nil worked. Why?

1

There are 1 answers

0
Rob On BEST ANSWER

I tried to set ta = nil or tb = nil, it didn't work,

This is because, as you point out, you have a "strong reference cycle" (formerly known as a "retain cycle"). This is the very definition of a strong reference cycle.

The TestA object that ta referenced still has a reference to the TestB object that tb originally referenced. Likewise, the TestB object that tb referenced still is keeping a strong reference to that TestA instance that ta originally referenced. So even after you set both the ta and tb pointers to nil, the actual objects that they originally pointed to are still keeping references to each other. Hence the cycle.

The key observation is that when you set ta and tb pointers to nil, it doesn’t do anything but removing your references to these TestA and TestB instances. But as long as there's something else maintaining a strong reference to these instances (in this case, they're maintaining strong references to each other), they won't be deallocated. We refer to the memory associated with those two objects as having been “abandoned”, i.e., you don’t have any references to them even though they’re not deallocated because they’re tied up in their mutual strong reference cycle.

The "Debug Memory Graph" feature, enter image description here, is really useful in visualizing this. So after I set ta and tb to both be nil, I looked at the memory graph and it shows that my ViewController no longer had reference to these two objects, but they're still both referencing each other:

enter image description here

but ta.b = nil worked. why??!!

This works (assuming you did this before setting ta to nil) because it breaks the strong reference cycle. When you set ta.b to nil, the TestB object no longer has any strong references and it can be deallocated. And, once that TestB instance is deallocated, it will remove its reference to that TestA instance, so that TestA instance will also be deallocated because the last strong reference to it will have been removed.


Perhaps needless to say, but the way you prevent this problem is to make one of the properties weak. For example, if TestA is the logical “parent” object, you’d probably make the b property in TestA a strong property, but make the a property in TestB a weak property. This resolves the strong reference cycle and eliminates this problem entirely.