Developer's Blog

【Objective-C】NSObject クラスと NSObject プロトコルが分かれているのはなぜ?

こんにちは。開発担当の金内です。

Mac / iOS アプリ開発で使う Objective-C におけるルートクラスと言えば、泣く子も黙る NSObject です。Java における java.lang.Object や Python における object にあたるものが、Objective-C では NSObject なわけです。

さて、この NSObject にはクラスとプロトコルの両方があることをご存じでしょうか?そして、NSObject クラスは NSObject プロトコルを採用しています。これらはなぜ分かれているのでしょうか?クラスだけでは困るのでしょうか?

分かれている真の理由は、泣く子も黙る NSObject を設計した人の深い深い考えがあってのことでしょう。私のような凡人が知るすべはないかもしれません。

しかし、NSObject プロトコルのありがたさを感じることはできます。そのためのサンプルを書いてみました。

#import <Foundation/Foundation.h>

// 独自定義のプロトコル
@protocol MyProtocol

- (void)doSomething;

@end

// MyProtocol を採用したクラス
@interface MyClass : NSObject <MyProtocol> {

}
@end

// MyClass の実装
@implementation MyClass

- (void)doSomething
{
    // ... but do nothing.
}

@end

// main 関数。ここが実行される。
int main (int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    id<MyProtocol> obj = [[MyClass alloc] init];
    [obj doSomething];
    [obj release];

    [pool drain];
    return 0;
}

独自プロトコルを定義して、そのプロトコルを採用したクラスのオブジェクトを id<MyProtocol> として参照し、利用しています。

何の変哲もない(機能もない)このコードをコンパイルしてみると、次のような警告が出ます。

warning: instance method '-release' not found (return type defaults to 'id') [3]
     [obj release];
     ^~~~~~~~~~~~~

「obj には release なんてメソッドなさそうだけど?」とのことです。

たしかに、MyProtocol は doSomething メソッドのみを宣言していて、release については触れていません。基本的にオブジェクトはすべて NSObject(のサブクラス)なのですから、当然、release メソッドはあるはずなのですが…

警告が出ないようにするためには、MyProtocol に release の宣言を追加しなければならないのでしょうか?同じように autorelease なども必要ということでしょうか?

そこで登場するのが、あの「NSObject プロトコル」です。NSObject プロトコルのリファレンスを見ると、問題となっている release や autorelease はもちろん、description や isEqual: といったメソッドが宣言されているのがわかります。

渡りに舟とはこのことですね。さっそく、MyProtocol を NSObject を継承したプロトコルにしてみましょう。

@protocol MyProtocol <NSObject> // NSObject を継承するように変更

これで id<MyProtocol> で参照したオブジェクトが release メソッドを持つことがわかって、コンパイルは何の警告もなく成功します。つまり、NSObject プロトコルを使えば独自プロトコルが他の基本的なメソッドも持っていることを簡単に宣言できるわけです。NSObject クラスしかなかったらこうはいきません。

いかがでしょうか? NSObject プロトコルのありがたさを感じていただけたでしょうか?

NSObject プロトコルのリファレンスには「If an object conforms to this protocol, it can be considered a first-class object(このプロトコルに適合していれば、第一級のオブジェクトと見なせる)」と書いてあります。つまり、NSObject プロトコルこそ、すべてのオブジェクトに共通するメソッドを宣言しているのです。NSObject プロトコルすごい!!

ふだんはあまり意識することのない NSObject プロトコル、実は縁の下の力持ちとしてフレームワークを支えてくれているわけです。

P.S. あまり触れる機会はないかもしれませんが、唯一 NSObject クラスを継承しない NSProxy というクラスがあります。これも NSObject プロトコルを採用しています。こういったクラスが存在できるのも、NSObject プロトコルのおかげですね。

Copyright © 2019 Fenrir Inc. All rights reserved.