読者です 読者をやめる 読者になる 読者になる

24/7 twenty-four seven

iOS/OS X application programing topics.

delegate オブジェクトは retain すべきではない

iPhone Objective-C Cocoa Mac

Delegating objects do not (and should not) retain their delegates. However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around to receive delegation messages. To do this, they may have to retain the delegate in memory-managed code. This precaution applies equally to data sources, notification observers, and targets of action messages. Note that in a garbage-collection environment, the reference to the delegate is strong because the retain-cycle problem does not apply.

Mac Developer Library

delegate オブジェクトはたいていの場合 retain メッセージを送信して保持してはいけません。


なぜなら、普通の使い方では delegate に代入されるオブジェクトはほとんどのケースで委譲元を保持 (retain) しているからです。
その状態で delegate オブジェクトを保持 (retain) してしまうと、循環参照 (retain cycle) に陥ってしまうので、どちらのオブジェクトも解放することができず、メモリリークが発生してしまいます。


例えば UITableView の場合を考えます。
普通、ビューコントローラが UITableView のインスタンスを生成し、デリゲートにはビューコントローラ自身を指定します。

- (void)loadView {
    UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 367.0f)];
    contentView.backgroundColor = [UIColor whiteColor];
    self.view = contentView;
    [contentView release];
    
    tableView = [[UITableView alloc] initWithFrame:contentView.frame style:UITableViewStylePlain];
    tableView.delegate = self;
    tableView.dataSource = self;
    [contentView addSubview: tableView];
    [tableView release];
}

このとき、 UITableView のインスタンスがデリゲート・オブジェクトを保持 (retain) するならば、テーブルビューとビューコントローラが互いの参照を保持しあう形になるので、どちらも解放することができなくなります。


上記の問題を避けるため、デリゲート・オブジェクトは通常 assign 属性として宣言します。

@property(nonatomic, assign) id delegate


Cocoa のクラスではほとんどのデリゲート・プロパティは assign 属性として宣言されていますが、わずかに例外があります。
ひとつは CAAnimation クラスのデリゲート・プロパティです。

@property(retain) id delegate

Important: The delegate object is retained by the receiver. This is a rare exception to the memory management rules described in Memory Management Programming Guide for Cocoa.
An instance of CAAnimation should not be set as a delegate of itself. Doing so (outside of a garbage-collected environment) will cause retain cycles.

Mac Developer Library

ドキュメントにも記述されていますが、稀な例外 (a rare exception) のひとつです。


他の例としては NSURLConnection に指定したデリゲートも NSURLConnection のインスタンスによって保持されます。