spacer
natsu_icon iPhoneアプリ開発に関する内容を中心とした、開発者Natsuの記録。
開発Tipsや読んだ本の紹介などなど。

Calender

      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
September 2010

Natsu's note Category


memory management の最新10件のエントリー
Objective-C メモリ管理 : 生成と解放

LINKS
NATSU'S NOTE TOP
PROFILE
CONTACT

twitter.com/natsun_happy
MediaMarker

MoMAstore

ボーズ・モバイルヘッドセット

ウェーブミュージックシステム

ヤマギワ オンラインストア

09.11.29 Objective-C メモリ管理 : 生成と解放


Objective-Cを扱い始めて最初に戸惑ったのは、メモリの管理だ。
慣れるまでだいぶ苦労したので、ポイントをまとめておこうと思う。

ただし、ここにまとめる内容は超基本事項。とくにすばらしい機能ではない。。
しかしながら、数ヶ月ほどほかの言語を書いていると案外忘れてしまうものなので(汗)、自分のためにまとめておく。

そもそもリファレンスカウンタとは何か。

リファレンスカウンタ方式のメモリ管理では、そのインスタンスが何カ所から参照されているかを示すカウンタを用いてメモリを管理する。
同じインスタンスを複数のオブジェクトが参照しているとしても、複数個、同じオブジェクトを作成するのではなく、同一のオブジェクトを全員が参照するのだ。
はじめはだいぶ戸惑ったこの方式、慣れてしまえばお手の物。

インスタンスの生成と解放

何はともあれ、あるオブジェクトを使いたければ、インスタンスを生成をしなくてはならない。そして、使い終わったらもちろん解放(破棄)する必要がある(当たり前だが、解放しないと恐怖のメモリリーク!)。
ここでポイントとなるのは、生成の方法によって解放の仕方が異なること。
これを知らずに手探りで生成や解放をするのはとても危険。

  生成 オーナーシップ 解放
一般的なインスタンス init または copy 生成元 release
一時的なインスタンス クラス名で始まるインスタンス変数 なし 不要
このほかに、解放しないインスタンス変数も存在するが(例:シングルトンパターンの実現用)これについては別途まとめることにする。

例として、NSStringクラスのオブジェクトを生成、解放する場合、以下のようになる。

一般的なインスタンスオブジェクト

NSString *string = [[NSString alloc] initWithString:@"hoge"];
.......
.......
[string release];

生成されたオブジェクトのオーナーシップは、生成元が持つことになる。
したがって、生成元が解放まで責任を持つ必要がある。

また、init(もしくはcopy)にて生成されたオブジェクトは、その時点でリファレンスカウンタが+1となっているため、新たにretainを送る必要はない

一時的なインスタンスオブジェクト

NSString *string = [NSString stringWithString:@"hoge"];

生成されたオブジェクトは、その時点で、自動解放プールに登録されているため、自ら解放する必要はない(してはいけない)

一時的なインスタンスオブジェクトを生成するためのクラスメソッドは多数用意されている。
NSStringやNSArrayのクラスメソッドは中でもよく目にするものではないだろうか。

インスタンス生成方法の使い分け

一般的なオブジェクトと、一時的なオブジェクト、どのように使い分けるのがいいのだろう。

Objectiv-Cの勉強を始めた頃、この二つの方法をうまく使い分けられていなかった気がする。
一見、一時的なオブジェクトを生成しておけば、release忘れによるメモリリークもないし、なんだか安心な気がしていた。
が、それは大きな間違い。

なぜか。
メモリには限りがあるから!(ここから先は、iPhoneアプリの開発に限った話)

何も注意しないでいると、iPhoneアプリの自動解放プールは、アプリ起動時にひとつ作られ、アプリ終了時に解放される。
つまり、ほとんど意味をなさない。。。
この状態で、一時的なオブジェクトを生成し続けると、結果、メモリ不足に悩まされるだろう。

そこで二つの解決方法が考えられる。

生成、解放をこまめに行う

まず一つ目の方法は、もちろん、生成と解放をこまめに行うこと。
むやみやたらに、stringWith... や arrayWith... などというクラスメソッドに頼らず、その都度、alloc と init を組み合わせてオブジェクトを生成する。
そして、使用後は必ずreleaseメッセージを送って解放する。

独自の自動解放プールを持つ

例えば、複数回(かなり多くの回数)まわる for ループの中で、大量のNSStringオブジェクトを生成して利用する必要がある場合を考えてみる。この場合、その都度オブジェクトの生成と解放を繰り返していてはオーバーヘッドも大きくなるだろう(推測)。
だからといって、すべてを一時的なインスタンスオブジェクトとして生成していると、メモリ不足となりアプリが強制終了してしまったり、動作が不安定になったりして困ることも。

そんなときは、ループ内で独自の自動解放プールを持つとよい。そして、ループを抜けるときに、この自動解放プールを解放する。

while (...) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    /* 一時的なオブジェクトを多数生成 */
    (例)
    NSString *string1 = [NSString stringWithString:@"hoge"];
    .....
    .....
    .....

    [pool release];
}

これで、ループ内でメモリが枯渇してしまうことは避けられる。

デバッグ時の注意

メモリまわりのデバッグをするときに注意したいこと。
それは、できるだけ実機テストを行うということ。
シミュレータでテストしていては、メモリは枯渇しない(仮にしたとしたら、実機では全く動作しないでしょう・・・)。

メモリ周りの問題はやっかいなことが多い。なかなか簡単に片付けることができなかったり、ときとして、設計変更を余儀なくされたり。。
開発終盤でこれでは辛いので、できるだけ早い段階で、メモリに関する設計ミスがないかは確認しておきたいところ。

静的解析の利用

以前にまとめたXcodeのツールであるBuild and Analyzeを使えば、生成と解放に関する記述ミスをある程度見つけてくれる。
例えば、initを使って生成したのにreleaseしていない、もしくは、一時オブジェクトとして生成したのにreleaseを送っている、など。

この二つを見つけられるだけでも、メモリリークや不正アクセスをかなり防ぐことができるので、開発初期段階ではこまめにチェックをしたいところ。

参考:iPhoneアプリ開発:XcodeのBuild and Analyze

参考図書

メモリ周りに関しては、知らなくてもある程度は動作してしまう。でも、ソフトウェアとしての品質を追求していくためには、絶対に押さえておくべきことと信じている私。
ということで、、、必死に勉強しました。以下、絶対おすすめ!

詳解 Objective-C 2.0
¥ 4,410 / ソフトバンククリエイティブ (2008-05-28)
在庫あり。

C++、javaは経験済みで新たにiPhoneアプリ開発に着手しようと手に取った本でしたが オブジェクト指向、クラス、継承など重要な概念の説明箇所はもっと簡潔明瞭でいいと 思いました。 初心者にも優しい言い回しとして工夫されたと思うんですが、それがかえってややこしくし 分かりにくくなってます。 つまずいてしまうか、誤解してしまうかの危険があると思います。 一度誤って理解したり、苦手意識を持ってしまうと修正が困難で面倒なので 新人、後輩にはこの本は薦めたくないです。 オブジェクト指向言語についてすでに承知している人が手っ取り早くobjective-cを 押させてしまおうという場合なら助けになると思います。
...続きを読む



NatsuのiPhone, iPod touchアプリ
iLoanCalc iLoan Calc
ボーナス払い/繰上げ返済対応
ローンシミュレーションアプリ
App_Store_Badge_EN
clockBIN pro clockBIN pro
バイナリ時計&タイマー
(デジタル表示つき)
App_Store_Badge_EN