Retain Cycles and Weak References

ARC’s behavior is automatic and mindless; it knows nothing of the logic of the relationships between objects in your app. Sometimes, you have to provide ARC with further instructions to prevent it from doing something detrimental. One such detrimental thing is the creation of a retain cycle.

A retain cycle is a situation in which Object A and Object B are each retaining one another. Such a situation, if allowed to persist, will result in a leak of both objects, as neither object’s retain count can decrement to zero.Another way of looking at it is to say that Object A, by retaining Object B, is also retaining itself, and thus preventing its own destruction.

A retain cycle can arise quite innocently, because relationships in an object graph can run both ways. For example, in a system of orders and items, an order needs to know what its items are and an item might need to know what orders it is a part of, so you might be tempted to let it be the case both that an order retains its itemsand that an item retains its orders. That’s a retain cycle.

To illustrate the problem, I’ll suppose a simple class MyClass with a single ivar _thing and a single public setter setThing:, with logging in dealloc, like this:

@implementation MyClass {

    id _thing;

}

- (void) setThing: (id) what {

    self->_thing = what;

}

-(void)dealloc {

    NSLog(@"%@", @"dealloc");

}

@end

We now run this code:

MyClass* m1 = [MyClass new];

MyClass* m2 = [MyClass new];

m1.thing = m2;

m2.thing = m1;

m1 and m2 are now retaining one another, because ARC retains on assignment by default. When the code runs, dealloc is never called for either of our MyClass instances, even after the automatic pointer variables m1 and m2 have gone out of scope and have been destroyed. The two MyClass objects themselves have leaked.

You can prevent an instance variable from retaining the object assigned to it by specifying that the instance variable should be aweak reference. You can do this with the __weak qualifier in the instance variable’s declaration:

@implementation MyClass {

    __weak id _thing;

}

Now there is no retain cycle. In our example, there will be no leak; the two MyClass objects will go out of existence after our code finishes running, because ARC sent them each a release message as the automatic variables m1 and m2 went out of scope (to balance the new calls that created them), and no one else is retaining them


In ARC, a reference not explicitly declared weak is a strong reference. Thus, a strong reference is one where ARC retains as it assigns. There is in fact a __strong qualifier, but in practice you’ll never use it, as it is the default. (There are also two additional but rarely needed qualifiers, __unsafe_unretained and __autoreleasing.)




In real life, a weak reference is most commonly used to connect an object to its delegate (Chapter 11). A delegate is an independent entity; there is usually no reason why an object needs to claim ownership of its delegate, and indeed an object is usually its delegate’s servant, not its owner. Ownership, if there is any, often runs the other way; Object A might create and retain Object B, and make itself Object B’s delegate. That’s potentially a retain cycle. Therefore, most delegates should be declared as weak references.



For example, in a project created from Xcode’s Utility Application project template, you’ll find this line:

@property (weak, nonatomic) id <FlipsideViewControllerDelegate> delegate;

The keyword weak in the property declaration, as I’ll explain more fully later in this chapter, is equivalent to declaring the _delegate instance variable as __weak.

In non-ARC code, a reference can be prevented from causing a retain cycle merely by not retaining when assigning to that reference in the first place; the reference simply isn’t memory-managed at all. You may see this referred to as a weak reference; it is not, however, quite the same thing as an ARC weak reference. A non-ARC weak reference risks turning into a dangling pointer when the instance to which it points is released (by someother object that was retaining it) and is destroyed. Thus it is possible for the reference to be non-nil and pointing to garbage, so that a message sent to it can have mysteriouslydisastrous consequences.

Amazingly, however, this cannot happen with an ARC weak reference. When an instance’s retain count reaches zero and the instance is about to vanish, any ARC weak reference that was pointing to it is automatically set to nil! (This amazing feat is accomplished by some behind-the-scenes bookkeeping: when an object is assigned to a weak reference, ARC in effect notes this fact on ascratchpad list.) This is yet another reason for preferring to use ARC wherever possible. ARC sometimes refers to non-ARC weak references,disdainfully but accurately, as “unsafe.” (Non-ARC weak references are in fact the __unsafe_unretained references I mentioned a moment ago.)

Unfortunately, large parts of Cocoa itself don’t use ARC. Cocoa’s memory management is carefully written, so that in general its retains and releases are balanced and it shouldn’t cause any memory leaks. Nevertheless, properties of built-in Cocoa classes that keep weak references are non-ARC weak references (because they are old and backwards-compatible, whereas ARC is new). Such properties are declared using the keyword assign. For example, UINavigationController’s delegate property is declared like this:

@property(nonatomic, assign) id<UINavigationControllerDelegate> delegate

Thus, even though your code is using ARC, the fact that Cocoa’s code isnot using ARC means that memory management mistakes can still occur. A reference such as a UINavigationController’s delegate can end up as a dangling pointer, pointing at garbage, if the object to which that reference was pointing has gone out of existence. If anyone (you or Cocoa) tries to send a message by way of such a reference, the app will then crash — and, since this typically happens long after the point where the real mistake occurred, figuring out the cause of the crash can be quite difficult. The typical signs of such a crash are that it takes place in Apple’s method objc_retain and mentions EXC_BAD_ACCESS (Figure 12-1). This is the sort of situation in which you might need to turn on zombies in order to debug, as described earlier in this chapter.


Defending against this kind of situation is up to you. If you assign some object to a non-ARC weak reference, such as a UINavigationController’s delegate, and if that object is about to go out of existence at a time when this reference still exists, you have a duty to assign nil (or some other object) to that reference, thus rendering it harmless.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值