UIViewController でメモリの確保と破棄を行う際に viewDidUnload と dealloc をどのように使い分けたらよいか間違って覚えている人が多いように思います。

Web上にあるサンプルや書籍でさえも間違っているものが多くあります。

本記事では、UIViewController の viewDidUnload と dealloc の使い分けを整理していこうと思います。

UIViewController のメモリ管理まとめ

メモリの確保と破棄

UIViewController 内で確保したメモリの破棄場所は、結構ややこしいです。とくに viewDidUnloaddealloc の違いは抑えておく必要があります。

NSObject のメモリ管理の定石である init で確保したメモリは dealloc で破棄することは間違えないのですが UIViewController では viewDidload で確保したメモリを viewDidUnload で破棄して dealloc で破棄しない例がよくあるので間違えないようにしなければなりません。

viewDidload で確保したメモリは dealloc でも破棄しなければならない

viewDidload と viewDidUnload は名前が似ているため対のメソッドのように思いがちですがそうではありません

viewDidUnload

viewDidUnload はメモリ不足警告をアプリが受け取った場合に didReceiveMemoryWarning メソッドから呼び出されるものです。

メモリ不足警告が起こらない場合には viewDidUnload は呼び出されません。なので、viewDidload で確保したメモリの破棄処理を viewDidUnload のみに記述し dealloc に記述しないと、メモリ不足警告がでない場合にメモリの破棄がされません。

メモリの確保と破棄の正確な例

UIViewController でのメモリ確保と破棄の定石は次のようになります。

  • init で確保したメモリは dealloc で破棄する
  • viewDidload で確保したメモリは viewDidUnload と dealloc で破棄する

なお、init と viewDidload の使い分けは次のような感じで行えばよいとおもいます。

init

インスタンス変数の初期化処理

viewDidload

ビューが作成された際に行う処理。動的に画面部品を作成したり、サービス(現在位置の取得等)を実行したりする

サンプルコード

インスタンス変数 myData 、locationManager をそれぞれ init と viewDidload で初期化します。

locationManager_ は viewDidUnload と dealloc でそれぞれ初期化し、viewDidUnload の方では release 後に nil を設定しています。

これは、nil を設定せずに他の場所で呼び出されるとメモリアクセスエラーが発生するためです。

Objective-C は nil に対しての呼出は何も起こらないことになっているので、viewDidUnload で nil を設定しておくことで dealloc で再度 release しても問題がでないようにしています。

//
//  SampleViewController.m
//  SampleApp
//
//  Created by 濱田 章吾 on 10/10/18.
//  Copyright 2010 hamasyou. All rights reserved.
//
 
#import "SampleViewController.h"
 
@implementation SampleViewController
 
@synthesize myData = myData_;
@synthesize locationManager = locationManager_;
 
// Interface Builder で作成する UIViewController の指定イニシャライザ
- (id)initWithCoder:(NSCoder *)aDecoder {
  if ( (self = [super initWithCoder: aDecoder]) ) {
    myData_ = [[NSMutableArray alloc] init];
  }
  return self;
}
 
- (void)viewDidLoad {
  [super viewDidLoad];
  
  if (!locationManager_) {
    locationManager_ = [[CLLocationManager alloc] init];
  }
  locationManager_.delegate = self;
  locationManager_.desiredAccuracy = kCLLocationAccuracyBest;
  locationManager_.distanceFilter = 100;
  [locationManager_ startUpdatingLocation];
}
 
- (void)viewDidUnload {
  [locationManager_ release];
  locationManager_ = nil;
  
  [super viewDidUnload];
}
 
- (void)dealloc {
  [myData_ release];
  [locationManager_ release];
 
  [super dealloc];
}
 
@end