ガベージコレクションの導入
Objective-C 2.0でまず検討したいのは、ガベージコレクションだ。ガベージコレクションによって、Cocoaのプログラミングがどのように変わるか、説明しよう。
最も分かりやすいのが、アクセッサに関するものだ。たとえば、あるクラスに_nameというインスタンス変数があるとする。これに対するgetterとsetterを実装することを考えよう。それぞれ、name、setName:というメソッドにする。
単純に考えれば、それぞれインスタンス変数を返したり、代入してやればいい。だが、これでは問題がある。
// Objective-C 1.0では問題がある
- (NSString*)name
{
return _name;
}
- (void)setName:(NSString*)name
{
_name = name;
}
setName:で、何もせずに_nameにnameを代入しているが、これでは_nameに入っていた古いオブジェクトが解放されずにリークしてしまう。そこで、代入の前にreleaseを呼ぶようにしてみる。また、新たに代入するnameは、retainして保持しておこう。
// retain/releaseでリークを防ぐ
- (NSString*)name
{
return _name;
}
- (void)setName:(NSString*)name
{
[_name release];
_name = [name retain];
}
これでもまだ問題がある。setName:を呼ぶときに、_nameとnameが同じオブジェクトだった場合、[_name release]を呼んだ時点で解放されてしまう。そこで、_nameとnameが別のオブジェクトであることを確認しなくてはいけない。
// 同一オブジェクトに対応する
- (NSString*)name
{
return _name;
}
- (void)setName:(NSString*)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
さらに、マルチスレッドに対応することを考えよう。この場合、まず@synchronized指示子を使って同期を行なわなくてはいけない。さらに、他のスレッドで_nameオブジェクトにアクセスしている間、それが解放されないことを保証しなくてはいけない。そのために使われるのが、nameメソッドで、一度_nameをretainして、さらにautoreleaseをかける、というテクニックだ。これを行うと、少なくともこのメソッドを呼んだスレッドではしばらくの間、つまり自動解放プールの処理が行われるまで、は、オブジェクトが存在することが保証される。
// マルチスレッドを考慮する
- (NSString*)name
{
@synchronized(self) {
return [[_name retain] autorelase];
}
}
- (void)setName:(NSString*)name
{
@synchronized(self) {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
}
これが、Objective-C 1.0までのアクセッサメソッドだ。どう譲歩しても、分かりやすいとは言えない。
では、ガベージコレクションを利用してみよう。Objective-C 2.0では、このアクセッサメソッドが次のようになる。
// Objective-C 2.0でのアクセッサメソッド
- (NSString*)name
{
return _name;
}
- (void)setName:(NSString*)name
{
_name = name;
}
メモリのリークに関する処理、同一オブジェクトへの対応、マルチスレッド対応といったものを、すべてガベージコレクションが対応してくれるのだ。Objective-C 1.0までのCocoaでは、アクセッサ1つ作るのにもメモリ管理を理解する必要があり、慎重な設計が必要だった。これが、ガベージコレクションに導入で、ようやく気楽に作れるようになったのだ。
実際には、プロパティを使うことによって、さらに楽をすることが出来る。これについては、後述しよう。