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?
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?
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 thatta
referenced still has a reference to theTestB
object thattb
originally referenced. Likewise, theTestB
object thattb
referenced still is keeping a strong reference to thatTestA
instance thatta
originally referenced. So even after you set both theta
andtb
pointers tonil
, 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
andtb
pointers tonil
, it doesn’t do anything but removing your references to theseTestA
andTestB
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, , is really useful in visualizing this. So after I set
ta
andtb
to both benil
, I looked at the memory graph and it shows that myViewController
no longer had reference to these two objects, but they're still both referencing each other:This works (assuming you did this before setting
ta
tonil
) because it breaks the strong reference cycle. When you setta.b
tonil
, theTestB
object no longer has any strong references and it can be deallocated. And, once thatTestB
instance is deallocated, it will remove its reference to thatTestA
instance, so thatTestA
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, ifTestA
is the logical “parent” object, you’d probably make theb
property inTestA
astrong
property, but make thea
property inTestB
aweak
property. This resolves the strong reference cycle and eliminates this problem entirely.