今年の重要ニュース、いやここ数年でのObjective-Cでの最重要ニュースといえば、LeopardでのObjective-C 2.0の登場だろう。Objective-C 2.0で拡張された仕様と、それらがもたらすプログラミングの変化は、特集記事にまとめたので、まずはそちらを参照してほしい。
本連載でも、Objective-C 2.0を緊急的に取り上げていきたい。そこで、前回まで続いていたデザインパターンの話を一時中断して、Objective-C 2.0の解説をしていくことにしよう。特集記事の内容を踏まえた上で、さらに深くObjective-C 2.0に切り込んでいく。
では、まず早速Darwinに行き、Objective-C 2.0ランタイムのソースコードをダウンロードしよう……と、思ったのだが、現時点ではまだ公開されていないようだ。後日ポストされるというアナウンスがあるので、ソースコードの解析はそれまで待つとしよう。
NSGarbageCollectorクラス
Objective-C 2.0での最大の変更点は、ガベージコレクションの導入だ。そこで、まずはFoundationに導入された、ガベージコレクション関係のAPIを見てみよう。
ガベージコレクションを制御するために新たに追加されたクラスが、NSGarbageCollectorだ。そのものズバリの名前である。
このクラスは、まずガベージコレクションの有効/無効の切り替えが出来る。また、動作状況を調べることもできる。
NSGarbageCollector.h
- (void)enable;
- (void)disable;
- (BOOL)isEnabled;
- (BOOL)isCollecting;
enable、disableで、有効/無効を切り替える。isEnabledでそれらの状態を調査出来る訳だ。isCollectoingは、ガベージコレクションが動作中かどうかを調べる。
強制的にガベージコレクションを行わせることも出来る。collectExhaustively、collectIfNeededを使う。
NSGarbageCollector.h
- (void)collectExhaustively;
- (void)collectIfNeeded;
collectExhaustivelyは強制的にガベージコレクションを行い、collectIfNeededはメモリの消費量がある上限を超えている場合行うようである。メモリを大量に消費するソースコードを書く場合は、これらを使うことである程度の制御を行えるようだ。
これらのメソッドは、ランタイムAPIであるobjc_collect()を呼び出しているようだ。このAPIは、オプションとして次の値を設定出来る。
objc/objc-auto.h
enum {
// choose one
OBJC_RATIO_COLLECTION = (0 << 0), // run "ratio" generational collections, then a full
OBJC_GENERATIONAL_COLLECTION = (1 << 0), // run fast incremental collection
OBJC_FULL_COLLECTION = (2 << 0), // run full collection.
OBJC_EXHAUSTIVE_COLLECTION = (3 << 0), // run full collections until memory available stops improving
OBJC_COLLECT_IF_NEEDED = (1 << 3), // run collection only if needed (allocation threshold exceeded)
OBJC_WAIT_UNTIL_DONE = (1 << 4), // wait (when possible) for collection to end before returning (when collector is running on dedicated thread)
};
OBJC_EXPORT void objc_collect(unsigned long options);
ドキュメントが存在しないのでコメントから推測するしかないのだが、collectExhaustivelyメソッドはOBJC_EXHAUSTIVE_COLLECTIONに、collectIfNeededはOBJC_COLLECT_IF_NEEDEDに、相当するようだ。
OBJC_EXHAUSTIVE_COLLECTIONでは、フル、つまりおそらく世代を無視した、ガベージコレクションを行い、メモリの使用量がある程度回復したところで停止するようだ。完全なガベージコレクションを行うには、OBJC_FULL_COLLECTIONを使う必要があるらしい。
ガベージコレクションのためのランタイムAPI
ついでなので、objc_collect()以外の、ガベージコレクションに関するランタイムAPIをいくつか紹介しておこう。次のようなものがある。
objc/objc-auto.h
OBJC_EXPORT BOOL objc_collectingEnabled(void);
/* Tells collector to wait until specified bytes have been allocated before trying to collect again. */
OBJC_EXPORT void objc_setCollectionThreshold(size_t threshold);
/* Tells collector to run a full collection for every ratio generational collections. */
OBJC_EXPORT void objc_setCollectionRatio(size_t ratio);
/* Tells collector to start collecting on dedicated thread */
OBJC_EXPORT void objc_startCollectorThread(void);
順にガベージコレクションが有効かどうかの調査、ガベージコレクションを発動させるメモリの閾値の設定、ガベージコレクションを発動させる世代間の比率の設定、ガベージコレクションの発動、といったところだろう。これらのAPIにはドキュメントがないので、つまりプライベートAPIという位置づけか?、コメントからの憶測になる。
これらの詳しい挙動は、ソースコードが公開されたら、探っていきたい。