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?
I tried to set
ta = nil
ortb = 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, , 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:
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.