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
TestAobject thattareferenced still has a reference to theTestBobject thattboriginally referenced. Likewise, theTestBobject thattbreferenced still is keeping a strong reference to thatTestAinstance thattaoriginally referenced. So even after you set both thetaandtbpointers 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
taandtbpointers tonil, it doesn’t do anything but removing your references to theseTestAandTestBinstances. 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
taandtbto both benil, I looked at the memory graph and it shows that myViewControllerno longer had reference to these two objects, but they're still both referencing each other:This works (assuming you did this before setting
tatonil) because it breaks the strong reference cycle. When you setta.btonil, theTestBobject no longer has any strong references and it can be deallocated. And, once thatTestBinstance is deallocated, it will remove its reference to thatTestAinstance, so thatTestAinstance 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, ifTestAis the logical “parent” object, you’d probably make thebproperty inTestAastrongproperty, but make theaproperty inTestBaweakproperty. This resolves the strong reference cycle and eliminates this problem entirely.