自分自身が最初に勉強した時もそうでしたが、iPhoneアプリケーションの開発方法を新しく勉強するにあたって、最初の難関はメモリー管理の部分にあります。周りで勉強をはじめた人のコードを読んでいても、メモリーリークやリリースし過ぎているケースをよく見ます。
iPhoneにおけるメモリ管理は非常に重要な問題で、正しく学ぶためには時間をかけて詳解Objective-C 2.0等を熟読して内部の動きを理解する必要があると思うのですが、本だけを読んで理解するには結構なエネルギーと時間と想像力が必要だと思います。(そのために一回読んだだけでは把握しきれず、上記のような実装上の問題を抱えてしまっているように思います)。スムーズに勉強を進めるためには、色々な資料があった方が理解しやすいと思うので、ここでは自分が重要だと思うCocoaにおけるメモリ管理のポイント/具体的な作法を超適当に記述していきます。決してこの記事だけですべてを理解できるわけではないですが、メモリ管理のやり方を理解するための一助となれば幸いです。
1. インスタンス生成の基本はalloc -> init
Cocoaではオブジェクトのインスタンス生成はalloc -> initを連続して呼ぶのが基本です。
Foo *foo = [[Foo alloc] init];
allocするとメモリーが確保され、initの内部でメンバ変数などが初期化されます。allocしたままで初期化をせずに使うことは滅多にないので、基本的にはこの二つはセットで呼ぶものと考えておきましょう。
2. allocしたものはreleaseする
allocしたものは、使わなくなった段階でreleaseしてあげないとメモリー上に残ってしまいます。なので、用がなくなったタイミングでreleaseします。
Foo *foo = [[Foo alloc] init]; [foo hoge]; [foo release]; //必要なくなったので、解放します。
3. autoreleaseを使って、releaseしわすれを防ぐ
例えば、下記のようなコードがあったとします。
void hoge() { Foo* foo = [[Foo alloc] init]; if (a == YES) { [foo hoge]; if (b == YES) return; } [foo bar]; if (c == YES) return; [foo bar]; [foo release]; }
関数内に複数の条件分岐がでてくることは珍しくありません。条件によってはそこで処理を打ち切って値を返すような場合もよくあります。その時に、ついうっかり上記のようなコードを書いてしまうと条件によってはfooがリリースされないまま、メモリ上に残ってしまいます。
では、これをちゃんとreturnするまえにリリースするように書こうとすると、
void hoge() { Foo* foo = [[Foo alloc] init]; if (a == YES) { [foo hoge]; if (b == YES) { [foo release]; return; } } [foo bar]; if (c == YES) { [foo release]; return; } [foo bar]; [foo release]; }
と書く必要があり、結構めんどくさい。
AutoReleasePoolは、「このプールに突っ込まれたものは、関数が終わった後とかに自動でリリースしておいてあげるよ」という機構です。releaseする代わりに、autoreleaseを呼ぶとAutoReleasePoolにそのオブジェクトが突っ込まれて、関数が終わった後などに解放してくれます。これを使うと、上のプログラムは
void hoge() { Foo* foo = [[Foo alloc] init]; [foo autorelease]; //ここでは解放されない。あとで解放される。 if (a == YES) { [foo hoge]; if (b == YES) return; } [foo bar]; if (c == YES) return; [foo bar]; }
と書くことができます。とてもシンプル。厳密には関数が終わった後ではなく、ランループが一回りしたタイミングになりますが、そこらへんは詳解Objective-C 2.0等を読むなりして、復習してみてください。
AutoReleasePoolは便利ですが、正しく使うためにはランループの仕組みも理解する必要があります。ただ、賛否両論あると思いますが個人的な見解としては、実際問題シングルスレッドのシンプルなアプリケーションの開発ではその部分の仕組みを完全に理解していなくても問題はないと考えています。どっちにしろ、ここでランループとAutoReleasePoolの仕組みを詳しく書いていたら長くなってしまうので、割愛します。
長くなったので、続きは次の記事で。