ASとか

開発系の記事が多めです。タイトルのASはActionScriptの略です。

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に記述されているかと思います。

まとめ

最悪のケースは「viewDidLoadが複数回走り直すということは・・・?」に書いた、プロパティを介して代入していない&viewDidUnloadで破棄していないケースです。viewDidLoadは複数回繰り返される可能性のあるものという認識を持ち、気をつけて実装して下さい。