OSTRACISM CO.

C#とObjective-CとJavaと...

メモリ管理

 プログラムにとってもっとも基本的かつ重要な資源がメモリである。Cは処理系の単純さを目標とし、あらゆる機能の外部化(ライブラリ化)を果たしたため、他の言語では言語の機能に入っていることが多いメモリ管理もライブラリに任せている。mallocとfreeがそれだ。
 C++でnewとdeleteが導入され、メモリ管理は言語の機能になり、かなり面倒は減った。
 Javaはガベージコレクトという機能で、プログラマをメモリ管理の煩わしさから解放した、というよりはメモリ管理させてくれなくなった。C#も同様である。GraphicGripGropのJava版とC#版にはメモリ管理関連の記述は一切ない。
 Objective-Cはメモリ管理に関してはライブラリに任せている。単にmallocとfreeのクラス化されたものなら良かったのだがさにあらず、NSAutoreleasePoolというかなり厄介なクラスを前提としてCocoaのクラスが作られている。Cocoaでプログラムする限りNSAutoreleasePoolからは逃れられないのである。
 イベントが発生してプログラム中のコールバックに処理が渡ってきたときにNSAutoreleasePoolが自動的に有効になっている。Cocoaのクラスを普通に用意して使うと自動的にautorelease扱いになっており、処理がシステム側に戻るときにNSAutoreleasePoolの破棄が行なわれ、Cocoaのクラスのインスタンスも一緒に破棄される。
 イベントが終わっても維持したいCocoaのクラスは生成時にretain、破棄時にreleaseを呼び出すことになる。これ、結局手間はmallocとfree、あるいはnewとdeleteと同じ。まぁここまでは許せる。とはいえC++ではnewを使わないクラスのインスタンス化があり、実際メンバ変数のクラスはポインタでなく実体として定義することで普通に基本型の変数と同じように扱えるのでさほど煩わしくはないのだが、Objective-Cにはそういった仕様がない。メンバ変数のクラスインスタンスは明示的にretain、releaseしておかないと危険なのである。かなり厳しい仕様だ。
 普通はこの仕組みであまり困ったことにはならないのだが、「ループの中で大量に一時メモリを使う」ときに問題になる。たとえ一回に使うメモリが数MByteでも1000回ループしたら合計数GByteになるような処理で問題だ。
 C++ではループ内において、一時的なクラスを実体のローカル変数として宣言すれば必ずコンストラクタとデストラクタが呼ばれメモリが解放されるので、そもそも合計なんてことは考慮する必要がない。  ところがObjective-CというかCocoaではシステムに処理が戻るまでNSAutoreleasePoolによる破棄は全く行なわれない。不用意なコードを書くと実際にあるだけメモリを食いつくし、システムがフリーズに近い状態になる。

 TGraphicSearchクラスにまさしくそういう部分がある
- (BOOL)searchDirectorySub:(NSString *)dirPath {
	...
	for (i = 0; i < [files count]; i++) {
		...
		NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
		NSString *path = [files objectAtIndex: i];
		TFileInfo *current = [[TFileInfo alloc] initWithPath: path];
		[m_context progressCallback: current];
		if ([current makeThumbnail:m_pixelNumber]) {
			if ([m_target isEqualThumbnail:m_bits pardon:m_pardon info:current]) {
				[m_context hitItemCallback: current];
			}
		}
		[current release];
		[pool release];
	}
	...
}
 注目点はいろいろ処理をしている部分を挟むように
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
[pool release];
となっているところ。
 NSAutoreleasePoolは新しくインスタンス化するとそれがCocoaのクラスのカレントのメモリ管理者となり、明示的に破棄することでその管理下のCocoaのクラスのインスタンスを破棄することができる。

 はっきり言えばこのメモリ管理はCocoaの2つの大きな弱点のひとつだ。結局mallocとfreeの頃のプリミティブな方法からたいして変わっていない。いや、自由に扱えないという点でより一層厄介さが増しているような気がする。「カレントのメモリ管理者」という考え方も批判の多いカレントグラフポートみたいで洗練とはほど遠い。

2005.07.05
2005.09.18
「インデックス」へ戻る

OSTRACISM CO.
OSTRA / Takeshi Yoneki