这是代码:
TestA *ta = [[TestA alloc] init];
TestB *tb = [[TestB alloc] init];
ta.b = tb;
tb.a = ta;
我试图设置ta = nil或tb = nil。它没有用,但ta.b = nil有效。为什么?
这是代码:
TestA *ta = [[TestA alloc] init];
TestB *tb = [[TestB alloc] init];
ta.b = tb;
tb.a = ta;
我试图设置ta = nil或tb = nil。它没有用,但ta.b = nil有效。为什么?
我试图设置
ta = nil或tb = nil,它没有工作,
这是因为,正如您所指出的,您有一个“强参考周期”(以前称为“保留周期”)。这就是强参考循环的定义。
引用的TestA对象ta仍然具有对最初引用的TestB对象的tb引用。同样,引用的TestB对象tb仍然保持对最初引用的那个TestA实例的强ta引用。因此,即使您将ta和tb指针都设置为nil,它们最初指向的实际对象仍然保持对彼此的引用。因此循环。
关键的观察是,当您设置ta和tb指向 时,除了删除对这些和实例nil的引用之外,它不会做任何事情。但是只要有其他东西保持对这些实例的强引用(在这种情况下,它们保持对彼此的强引用),它们就不会被释放。我们将与这两个对象关联的内存称为“已被放弃”,即,即使它们没有被释放,您也没有对它们的任何引用,因为它们被绑定在相互强引用循环中。TestATestB
“调试内存图”功能
在可视化这方面非常有用。所以在我设置taand tbto both be之后nil,我查看了内存图,它表明我ViewController不再引用这两个对象,但它们仍然相互引用:
但
ta.b = nil工作。为什么??!!
这有效(假设您在设置ta为之前执行此操作nil),因为它打破了强引用循环。当您设置ta.b为nil时,TestB对象不再有任何强引用并且可以被释放。并且,一旦该TestB实例被释放,它将删除对该TestA实例的引用,因此该TestA实例也将被释放,因为对它的最后一个强引用将被删除。
或许不用说,但您防止此问题的方法是创建属性之一weak。例如,如果TestA是逻辑“父”对象,您可能会在b属性中TestA创建strong属性,但在a属性中TestB创建weak属性。这解决了强引用循环并完全消除了这个问题。