viewDidUnloadとdealloc
はじめに
このエントリは、メモリ管理でミスしないために - ASとかの説明です。同時に開きつつ見て下さい。
viewDidUnloadとは
deallocについての認識がずれていたケースは今まで無いのですが、viewDidUnloadについては、そもそも実装している人をあまり見かけません。しかしこれは大変な間違いで、メモリが足りなくなった際に優先的に解放されるべきオブジェクトが解放されないケースが出てきます。
deallocで破棄すべきオブジェクト
ここは問題ないと思っています。外から代入されたオブジェクトや、retain保持していたオブジェクト等を解放してあげて下さい。
viewDidUnloadで破棄すべきオブジェクト
タイミング
まずviewDidUnloadが走るタイミングですが、これはメモリ不足が起きた際、didReceiveMemoryWarningメソッドから呼び出されます。よくあるケースは、A画面 -> B画面へ遷移後、B画面でメモリ不足が起き、B画面に隠れて表示されていないA画面のviewDidUnloadを走らせるというものです。その後、A画面へ戻った際にはA画面のviewDidLoadが走り直し、画面を作成し直します。図とかなくて分かりにくいと思いますがすいません。
viewDidLoadが複数回走り直すということは・・・?
viewDidLoadで生成されたオブジェクトは、viewDidUnloadで破棄しなければ、いざメモリ不足が起こった際にメモリ節約ができないということです。さらに言えば以下のようなviewDidLoadの書き方をよく見ますが
- (void)viewDidLoad { [super viewDidLoad]; importer = [[DataImporter alloc] init]; // ほかにもいろいろ保持しまくり }
これはviewDidUnloadを実装していなければ、二回目のviewDidLoadでリークが発生します。
deallocとviewDidUnloadの違い
ここまで見て「じゃdeallocとviewDidUnloadは同じ処理を書けば良いんだね」と思った方もいるかもしれませんが、それは間違いです。外部から生成時に与えられたオブジェクトや、init時に自身で代入したオブジェクトなどは、viewDidLoadが走り直しても復活しません。つまり
viewDidUnloadにはviewDidLoadで生成したオブジェクトを破棄するコードを、deallocには保持している全てを破棄するコードを書かなければなりません。
やっとサンプルへ
サンプルでは以下のように記述しています。
// MemberListViewController.m - (void)dealloc { [self viewDidUnload]; self.leaderName = nil; [super dealloc]; } - (void)viewDidUnload { self.updateButton = nil; self.importer = nil; self.memberList = nil; [super viewDidUnload]; }
viewDidUnload内の破棄するコードはdeallocにも書く事になりますが、その際に記述が二重化しないようdeallocからは直接viewDidUnloadを呼んでいます。viewDidLoadで生成したオブジェクトや、IBOutletしているオブジェクトのみを破棄するコードがviewDidUnloadに記述されているかと思います。