前回の冒頭でも述べたとおり、Objective-C 2.0から(Mac OS X 10.5 から)、ガベージコレクションという自動メモリ管理の仕組みが導入されています。
この仕組みを利用すれば、メモリ上の不要なインスタンス(どこからも参照されていないインスタンス)は、必要に応じて自動的に解放されます。
ガベージコレクションはクルマのオートマチックミッションと同じで、たいへん便利であり、かつほとんどの場合に理にかなった動作を実現します。
ですが、極限まで燃料を節約したい(メモリ消費を抑えたい)場合や、とことんスピードを追求したい(アプリの動作を速くしたい)場合などには、マニュアルミッション(手動のメモリ管理)も有用となります。
また、Mac OS X 10.4以前やiPhoneなどのプラットフォームでは、そもそもガベージコレクションが利用できません。
ガベージコレクションを利用したサンプルを見てみましょう。ガベージコレクションを有効にするには、コンパイル時にオプション-fobjc-gc-onlyと明示する必要があります。コンパイルのコマンドは、例えば次のようになります。
gcc -fobjc-gc-only -o MyTestApp main.m -framework Foundation
上記のほかに -fobjc-gcというオプションもありますが、これは内部的にガベージコレクションに対応しないプログラムと共存させるためのオプションです。通常は -fobjc-gc-onlyで問題ないでしょう。それでは、以下のサンプルをコンパイルして実行してみてください。
01 #import <Foundation/Foundation.h> 02 03 @interface MyClass1 : NSObject { 04 } 05 @end 06 07 @implementation MyClass1 08 - (void)finalize { // (a) 09 printf("MyClass1 finalize calld.\n"); 10 [super finalize]; 11 } 12 @end 13 14 @interface MyClass2 : NSObject { 15 } 16 @end 17 18 @implementation MyClass2 19 - (void)finalize { // (a) 20 printf("MyClass2 finalize calld.\n"); 21 [super finalize]; 22 } 23 @end 24 25 void testFunction(void) { 26 MyClass1 *obj1 = [[MyClass1 alloc] init]; // (b) 27 } 28 29 int main(void) { 30 NSGarbageCollector *collector = 31 [NSGarbageCollector defaultCollector]; // (c) 32 printf("%d\n", [collector isEnabled]); // (d) 33 34 testFunction(); // (e) 35 36 MyClass2 *obj2 = [[MyClass2 alloc] init]; // (f) 37 38 [collector collectExhaustively]; // (g) 39 }
a. ガベージコレクションの環境では、クラスのインスタンスが解放される前にfinalizeメソッドが呼ばれます。これは開発者が明示的に呼び出すものではありません。
また、ガベージコレクションでインスタンスが解放されるタイミングは、ガベージコレクタ側が判断するものであり、開発者側には分かりませんので、finalizeメソッドに処理を実装する場合には十分に注意が必要です。
なおfinalizeメソッドをオーバーライドするときは、最後に必ずスーパークラスのfinalizeメソッドを実行してください。
b. ある関数のなかでMyClass1のインスタンスを生成してみます。このインスタンスは、この関数の終了と同時に不要となるはずです。
c. ガベージコレクションが有効になっているときは、NSGarbageCollectorクラスのdefaultCollectorメソッド(静的メソッド)で、動作中のガベージコレクタのインスタンスを取得できます。このインスタンスから、ある程度ガベージコレクションの動作を制御したり、動作状況を確認したりすることができます。
d. NSGarbageCollectorのインスタンスから、ガベージコレクションが有効になっているかどうか確認してみます。先ほどのオプションで正しくコンパイルされていれば、isEnabledメソッドが 1(true)を返すはずです。
e. 関数(testFunction)を実行しています。関数の終了後、MyClass1の不要なインスタンスがメモリ上に残ることになります。これはのちほどガベージコレクションで解放されるはずです。
f. 続いてmain関数内でもクラスのインスタンスを生成してみます。このインスタンスは、変数「obj2」として参照されているため、main関数が終了するまで(つまりアプリケーションが終了するまで)不要とは判断できません。従って、ガベージコレクションの対象とはならないはずです。
g. 先ほども述べたように、ガベージコレクションで実際に不要オブジェクトの解放が行われるタイミングは、ガベージコレクタに委ねられます。たいていの場合はそれでよいのですが、プログラムロジックの性質上、一時的に大量のインスタンスを生成せざるを得ない状況などもあります。そのようなときにガベージコレクションの実行を明示的に促すこともできます。
ガベージコレクションの制御は、先ほどcの部分で取得したNSGarbageCollectorのインスタンスで行うことができます。NSGarbageCollectorに、以下のメソッドが用意されています。
どちらもガベージコレクションに対して不要オブジェクトの解放を促すメソッドですが、名前が示すとおりcollectExhaustivelyのほうがより明示的に、強制的にメモリの解放を行います。ただし、ガベージコレクションの動作自体もアプリケーションの動作速度に影響しますので、乱用すべきではありません。
サンプルでは、ガベージコレクションの動作を確認するため、collectExhaustively で強制的にメモリを解放させています。出力結果から、testFunction関数内で生成され不要となったMyClass1のインスタンスが解放され、mainメソッドの文脈上まだ参照されているMyClass2のインスタンスは解放されないことが分かります。
ガベージコレクタにより、インスタンスの要・不要が判断されていることが、ごく簡単な例ですが確認できました。
8回に渡って、Objective-Cの基本について解説してきました。決して大きなボリュームではありませんでしたが、なるべく平易に、かつ網羅的にと心掛けて解説したつもりです。
ここではコンソール上で動作するごく簡単なプログラムに終始しましたが、多くの方は、MacやiPhoneで動作する、より本格的なGUIアプリケーションの開発を目指していることでしょう。
言語の基本的な動作イメージが頭に描けていると、複雑なアプリケーションを作成するときに、そのプログラムの骨格を理解するのに役に立ちます。GUIのパーツなども、ここで解説したような言語の仕組みの上に実現されているからです。
今後アプリケーション開発に取り組んでいくときに、折にふれて本稿をリファレンス的に参照していただけるようであれば幸いです。
Copyright © ITmedia, Inc. All Rights Reserved.