自动引用计数中的循环引用问题

9

我从未参与过非 ARC(自动引用计数)的项目。最近在我的 ARC 项目中遇到了一个僵尸对象。我发现这是由于循环引用导致的。我想知道什么是循环引用。

你能给我举个循环引用的例子吗?


4
一段代码的保留周期不会导致僵尸对象的出现。 - bbum
3个回答

24

循环引用是指对象 A 对象保留了对象 B,而对象 B 同时也保留了对象 A*。以下是一个例子:

@class Child;
@interface Parent : NSObject {
    Child *child; // Instance variables are implicitly __strong
}
@end
@interface Child : NSObject {
    Parent *parent;
}
@end

您可以使用__weak变量或weak属性来解决ARC中的保留循环问题,用于处理“反向链接”,即对象层次结构中指向直接或间接父级的链接:

@class Child;
@interface Parent : NSObject {
    Child *child;
}
@end
@interface Child : NSObject {
    __weak Parent *parent;
}
@end


* 这是保留循环最原始的形式;可能会有一长串的对象相互之间以圆形方式互相保留。


2
在ARC中,保留循环会导致内存泄漏吗? - Raj
@Raj 当然没错!保留循环不仅适用于ARC,也会在非ARC场景下导致内存泄漏。 - Sergey Kalinichenko
我们可以避免它们导致内存泄漏,就像我在@Simon_Germain回答的评论中解释的那样。 - trss

11

什么是保留循环?当两个对象相互引用并被保留时,就会创建一个保留循环,因为两个对象都试图保留彼此,这使得释放变得不可能。

@class classB;

@interface classA

@property (nonatomic, strong) classB *b;

@end


@class classA;

@interface classB

@property (nonatomic, strong) classA *a;

@end
为了避免ARC引起的保留循环,只需使用weak引用之一声明它们中的一个,就像这样:
@property (nonatomic, weak) classA *a;

不可能释放?如果我们小心重新分配某些其他对象到其中一个属性,打破链条,它就可以被释放了,对吧?我之所以特别问这个问题,是因为我经常听到“不可能释放”。 - trss
在http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html中,“避免保留循环规则#4:使用“close”方法来打破循环”这个标题似乎证实了可以打破保留循环。尽管最好避免,但如果它会在其他方面创建复杂性,则仍然可以使用,但必须小心在它们创建内存泄漏之前打破它,从而避免泄漏。 - trss
不可能可能有点夸张,但我们可以说这并不是一件轻而易举的事情。没有什么是不可能的。 - Simon Germain
我强调它不是不可能的原因是,当许多人说不可能时,试图对这样一个奇怪的概念有一个具体的理解会让人很难确定他们的理解是否正确,就像我遇到的情况一样。此外,打破这个循环是微不足道的。只需要简单地重新分配就可以打破它!如果 foo.bar = bar; bar.foo = foo; 创建了一个保留循环,那么简单的 foo.bar = baz; 就可以打破它,不会产生泄漏。对吧?这是我的理解。 - trss
@trss 这正是我一直在寻找的答案! - Evgeniy Kleban

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接