TweetOverviewのクラッシュレポートを解析して、気がつきました。
これは、ViewControllerのViewDidLoadメソッドが複数回呼ばれている故のクラッシュでした。
TweetOverviewでは、詳細画面から、メイン画面に戻るのですが、その際に、ViewDidLoadが再度呼ばれていました。といっても、Viewを再作成している訳ではなくて、[detailController dismissModalViewController…]から戻って来たときです。これは、どうも常識だったようですが、知りませんでした。。。昔のコード見直さないと。
iphone – When is viewDidLoad called? – Stack Overflow こちらを見ると、
You have to assume that viewDidLoad can be called multiple times. If there is a memory warning sent, your view controller will unload the view from memory, and the next time it is needed viewDidLoad will be called.
viewDidloadは、複数回呼ばれる事を予測するべきです。メモリの警告が送られると、ViewControllerは、一度viewをメモリから解放して、次にviewが必要になる時に、再度viewDidLoadを呼ぶことになります
との事です。つまり、view毎の状態を保存するなどのivarがある場合、viewDidLoad、viewDidUnloadなどでの、ivarの初期化、解放は危険だという事ですね。メモリの警告が発生される時に、バックグラウンドにあるビューがunloadされ、loadされる危険があります。はい、気をつけるとともに、昔のものを見直します。
対策方法
とはいえ、最初に表示される際のviewDidLoadは非常に便利なので、クラスのivarにdispatch_once_tで、一度だけ行うトークンを作成して、
@implementation ViewController{
dispatch_once_t onceToken;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
dispatch_once(&onceToken, ^{
tweetViews = [[NSCache alloc] init];
});
}
の用に記述して、初期化コードは、インスタンスに対して一度しか発生しない様に設定しました。
iOSのUIKitは、メモリ管理に関してもよく作られていますが、いろいろ注意点がありますね。
2012/5/15追記しました。
viewDidUnloadで開放しないリソースの確保ならinit系かawakeFromNibで初期化すべきでは? / “[UIKit]ViewDidLoad は一度しか呼ばれないと思っていました… | Zero4Racer PRO D…” htn.to/s5NvFb
— SAWA Tatsuhiroさん (@sawat1203) 5月 15, 2012
これからは、AwakeFromNib使います。こちらにも詳しく書いています。
cocoa touch – Which should I use, -awakeFromNib or -viewDidLoad? – Stack Overflow
Also important is that the awakeFromNib function will never be called after recovering from memory warning. But, viewDidLoad function will be called.
AwakeFromNibは、メモリ警告から復帰した時に呼ばれないけど、viewDidLoadFunctionは、呼ばれます。
描画に関係しない初期化は、AwakeFromNibが正解ですね。
「[UIKit]ViewDidLoad は一度しか呼ばれないと思っていました…」への1件のフィードバック