ガベージコレクションの導入

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つ作るのにもメモリ管理を理解する必要があり、慎重な設計が必要だった。これが、ガベージコレクションに導入で、ようやく気楽に作れるようになったのだ。

実際には、プロパティを使うことによって、さらに楽をすることが出来る。これについては、後述しよう。