@natsun_happy さんによる ARC解説シリーズ。今回は Outletの話。
[iOS5] ARC : Outletにはweakプロパティを使おう - iOS 開発ブログ Natsu's note
ざっくりまとめるとポイントは2点
- 通常 UIButton などのパーツは Onwerになる親ビューが存在するので weak が適している
- ただしトップレベルのビュー(どのビューにも属さない)の場合はOnwerとなる親ビューが存在しないので strongにする
(補足)
ARCが導入される前、以前のブログで retain か assign のどちらが Outletに適しているかを書いたことがある。
Cocoaの日々: UIView上のコントロールへの IBOutlet接続は retain で(assign改め)
その時の答えは retain 。
このブログから理由を引用する。
iPhoneの場合メモリ不足などが発生した場合、UIViewControllerが使用している UIView が開放される場合がある。前回の assign の場合、明示的に開放しなくても親ビューの開放と共にその上のコントロールも開放されるのでメモリリークの観点からは問題ない。しかし、UIViewの開放は意図しないタイミングで起こるために、誤って親ビューの解放後にコントロールへアクセスすることが起きうる。この場合、親ビューの開放に伴ってコントロールも開放されているので例外が出てクラッシュしてしまう(開放済みのオブジェクトへのアクセス)。また場合によっては親ビューの突然の開放があった場合でも入力中の文字列を退避したいケースがあるかもしれない。これらの理由により、コントロールのメモリ管理のオーナーシップを自分で持ってコントロールした方が良いと思われる。上記は Apple のリファレンスにも書いてあった...はずなのだがリファレンスから消えてる。。
(それはともかく)今回のように weak を使う場合は参照先が破棄された場合 zeroing によって nil が入るので、上記で懸念している問題は起きない。IBOutletを weak で持つというのは今後メリットのある方法だと思う。
ところで Appleが提供するサンプルコードはどうなっているのか。iOS Developer Library より最新(2011-10-31)かつ ARCを使っているサンプルを見てみた。
iAdSuite
この中の TextViewController.h を見てみる。
iAdSuite: ContainerBanner/ContainerBanner/TextViewController.h
ヘッダの定義。
@property(nonatomic, strong) IBOutlet UIView *contentView; @property(nonatomic, strong) IBOutlet UITextView *textView; @property(nonatomic, strong) IBOutlet UILabel *timerLabel;
XIB
トップレベルでは無いが strong を使っている。
また viewDidUnload でそれらを開放している。
- (void)viewDidUnload { [super viewDidUnload]; self.contentView = nil; self.textView = nil; self.timerLabel = nil; }
従来のやりかただ。
ただ以前から存在したコードをコンバートしたようなので上記のようになっている可能性が高い。
このあたり今後新規に書き起こすサンプルがどうなっているのか今後見ていく。
(2011-11-25 追記)@natsun_happyさんからの指摘で Apple が Outlet を weak で保持することを推奨していることがわかった(私が見落としていただけですが)。
ドキュメント:
Resource Programming Guide: Nib Files
"Managing the Lifetimes of Objects from Nib Files" の中でこう述べられている。トップレベルのオブジェクトを除き weak にすべき、と。その理由として
From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong
Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.とある。このあたりは @natsun_happy さんの説明の通り。
The strong outlets are frequently specified by framework classes (for example, UIViewController’s view outlet, or NSWindowController’s window outlet).
また従来推奨していた方法の説明が "Legacy Patterns" に書いてある。iOS では従来 retain が推奨されていた。
For iOS, you should use:
@property (nonatomic, retain) IBOutlet UserInterfaceElementClass *anOutlet;