C++とObjective-Cで、クラスを定義してみる。
C++はC言語の構造体 (struct) を拡張してクラスをつくり、インスタンスへのポインタでオブジェクトを指すことにした。Objective-Cは、オブジェクトを指すid型を導入した。
次の例はC++で書いた多態 (polymorphism) の例。C++では派生クラス(サブクラス)Bar のメソッドを呼ぶためには、メソッドにvirtualを付けなければならない。
次に、Objective-Cで上記のC++での内容とほぼ同じものを書いてみる。
ヘッダファイルの拡張子は.h、実装は.mにしなければならない。
Objective-Cではクラス宣言とクラス定義が明確に分かれている。クラス宣言は、@interface 〜 @end のなかにインスタンス変数宣言、メソッド宣言を書く。Cのソースのなかでかなり浮いて見える。。
ルートクラスは、gccを使う場合は、Objectとなる。<objc/Object.h>で宣言されている。
メソッド宣言は、{ }の外で行う。宣言の書き方は、C++と、あるいはCの関数宣言ともずいぶん異なる。
- (戻り値の型) メソッド名 :(引数の型) 引数 ... ;
先頭の「-」はインスタンスメソッドをあらわす。クラスメソッドのときは「+」とする。戻り値の型、引数の型は( )で囲む。また、引数の前には「:」を付ける。型を省略したときはid型とみなされる。
次のソース(部分)は、クラスFooを宣言する。
クラスの実装は、@implementation 〜 @end のなかに書く。通常は、クラス宣言はヘッダファイルで、実装は.mファイルでする。
メソッドの定義部は、普通のC言語の関数のように書けばいい。戻り値の型を省略したときはid型になるので、自分(インスタンス)自身を指すself を返すようにすればいい。
さきほどのC++と同様に、派生クラスを作ってみる。Objective-Cでは、メソッドを追加せずにオーバーライドするだけなら、@interface 〜 @end にメソッドを書く必要はない。@implementation で新しい定義を書くだけでいい。
このようにして定義したクラスのインスタンスを生成し、メソッドを呼び出すには、次のように書く。C言語の関数呼び出しとえらく違いすぎて、ここでも浮いた感じになってしまうが。
[ レシーバ メソッド :引数 ... ]
インスタンスを生成するには、alloc
クラスメソッドを使う。
(2003.10.03追加。)
Objective-Cでは、C++と違い、呼び出すメソッドの解決を動的に行う。
C++(あるいはJavaでも)では、複数のクラスのインスタンスを操作する場合には、インターフェイスクラスをひとつ作って、具象クラスはそこから派生させる。これは、C++ではコンパイル時にクラス、メソッド名の解決をするため。
Objective-Cでは、同じメソッドを持つオブジェクトであれば、どんなクラスのオブジェクトであっても同じように扱うことができる。(Duck Typing)
次のソースは、クラスFooとクラスBarで同じ名前のメソッドmethod_aを定義している。FooとBarのあいだには、共通の親クラスは(ルートクラスであるObject以外には)ない。関数pでは、Foo、Barのいずれのインスタンスを渡されても、正しくそれぞれのクラスのメソッドを呼び出すことができる。
また、Bazクラスにはmethod_aメソッドがない。しかし、コンパイル時にはエラーにならず、実行時に次のエラーを出してSEGVする。
error: Baz (instance) Baz does not recognize method_a
このあたり、むしろRubyなどのスクリプト言語に近い印象がある。
15| #include <objc/Object.h> 16| 17| @interface Foo: Object {} -method_a; @end 18| @implementation Foo 19| -method_a { printf("Foo's method\n"); return self; } 20| @end 21| 22| @interface Bar: Object {} -method_a; @end 23| @implementation Bar 24| -method_a { printf("Bar's method\n"); return self; } 25| @end 26| 27| @interface Baz: Object {} @end 28| @implementation Baz 29| @end 30| 31| void p(id obj) { [obj method_a]; } 32| 33| int main() { 34| id obj = [Foo alloc]; p(obj); 35| obj = [Bar alloc]; p(obj); 36| obj = [Baz alloc]; p(obj); 37| return 0; 38| }