releaseの重要性

2010/09/01

アプリを作ってみて初めてreleaseの重要性を実感しました(^^;) CGContextでUITextField、UILabelを大量に追加したUIViewを往復してると、動作が重くなりメモリリーク(>_<)

UINavigationControllerでViewController(@synthesizeした)をpush後、親ページに戻った時(popした時)に、viewDidUnloadが発生しないことがわかりました。

メモリリークの遷移

その為、以下の遷移でを繰り返していると

(1) UIViewControllerをinitWithNibNameでallocし、UINavigationControllerに追加(pushViewController) (2) UIViewControllerのviewDidloadが呼ばれる (3) UIViewControllerに大量のオブジェクトをaddSubViewする (3) UINavigationControllerで戻る(popViewController) (4) UIViewControllerがdeallocされない

と、通常は2~3Mのメモリを行ったりきたりするのですが、ページにアクセスする毎に500K単位で増えていきました。

原因

(1) pushするUIViewControllerを@synthesizeして(せざるを得なかった)利用していた (2) pushするUIViewControllerをpush後にreleaseしていなかった(できなかった) (3) UIViewにaddSubViewしたオブジェクトをreleaseしていなかった

pushするUIViewControllerを@synthesizeしていた(せざるを得なかった)のは、自分の設計ミス(^^;)

リークする例1

@synthesize hogeViewController;
   ...
-(void)pageHoge {
   hogeViewController= [[HogeViewController alloc] initWithNibName="HogeViewController" bundle:nil];
   [self.navigationController pushViewController:hogeViewController animated:YES];
}

他のメソッドでViewControllerを利用したいばかりに、不用意に@synthesizeした結果です。 HogeViewControllerに追加したオブジェクトがアクセスするたびにallocされますが、UINavigationControllerで戻ってもviewDidLoadが呼ばれません。 よって全てのオブジェクトがメモリ内に残ったままです。

pushViewController後にrelease

-(void)pageHoge {
   HogeViewController *nextController = [[HogeViewController alloc] initWithNibName="HogeViewController" bundle:nil];
   [self.navigationController pushViewController:nextController  animated:YES];
   [nextController release];
}

それでも@synthesizeが必要なときは、nilで判断するかないのかな???

リークする例2

-(void)addButton {
    IButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button addTarget:self action:@selector(edit:) forControlEvents:UIControlEventTouchUpInside];
    [self addSubView:button];
}

単純にbuttonのrelease忘れです。

addSubViewする時はretainで

これも忘れがちですが、UIButton等を動的にaddSubViewする場合、retainされたオブジェクトにしておくことです。

-(void)addButton {
    UIButton *button = [[UIButton buttonWithType:UIButtonTypeCustom] retain]; //retain
    [button addTarget:self action:@selector(edit:) forControlEvents:UIControlEventTouchUpInside];
    [self addSubView:button];
    [button release];  //メモリ開放
}

retainしないでaddSubView後にreleaseするとアプリが落ちます。 これ外部の指摘で気づきました。 ※他にも多々そういうトピックがあるかと・・・ buttonWithType は init 系メソッドじゃないので、retain の必要ないので下記のコードで大丈夫です。

-(void)addButton {
    IButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button addTarget:self action:@selector(edit:) forControlEvents:UIControlEventTouchUpInside];
    [self addSubView:button];
}

まぁ、retain して release してもトータルプラマイゼロにはなると思いますが、意味がない・・・。

メモリ状況の確認

これらは、InstrumentsのLeaksを利用してメモリ変化が見られます。 instruments

勉強を始めた時には実感がなかったのですが、作ってみて初めて身にしみますw

参考サイト

メモリリーク対策のサイト ・How to avoid memory leaks in iPhone applications