Objective-C で生成したクラスのインスタンスは id 型の変数に保存することができました。 id 型は void * に似た存在で、クラスに関係なく、インスタンスを保存する汎用的なオブジェクト型です。 そのため、id 型の変数の実体は、コンパイル時ではなく実行時に判定されるという特徴があります。
しかし、実行時にインスタンスを調べるということは、例えばメソッドを呼び出すために、オブジェクトに対してメッセージを送信しても、そのメッセージに対応したメソッドが存在しないという可能性があります。 次のようなプログラムは、その典型的な例でしょう。
#import <stdio.h> #import <objc/Object.h> @interface A : Object - (void) Write; @end @implementation A - (void) Write { printf("I am the born of my sword.\n"); } @end int main() { id obj1 = [A new]; id obj2 = [Object new]; [obj1 Write]; [obj2 Write]; //実行時エラー return 0; }
このプログラムは、宣言した A クラスのインスタンスを生成し Write メッセージを送信しています。 同様に Object クラスのインスタンスを生成し、Write メッセージを送信しています。 Object クラスには Write メソッドなどは存在しませんが、コンパイルすることができます。 これは、Objective-C のメッセージの呼び出しが実行時に判定されることを表しています。
コンパイルされたプログラムを実行すると、obj1 変数に対する Write メッセージの送信は成功しますが、obj2 変数は Object クラスのオブジェクトなので Write メソッドは存在しません。 そのため、obj2 変数に対して Write メッセージを送信しても、実行時エラーが発生するのです。
このように、id 型は汎用的で、オブジェクトという単位でクラス型のインスタンスデータを参照するには便利なのですが、特定のクラスを想定して扱う場合には不向きです。 特に、大規模なプログラムになって、多くのインスタンスを生成するような場合は、id 型を大量に宣言すると、どの変数にどのクラス型のオブジェクトを保存しているのかどうかがわからなくなってしまいます。
型の判定を実行時ではなく、コンパイル時に行うことが出来れば、コンパイラが型をチェックする情報として利用する可能性があります。 実装に依存する問題ですが、型がコンパイル時に判明すれば、メッセージ式を調べて、対象のクラス型に指定したメソッドが存在するかどうかを調べ、必要に応じて警告を発生させられるでしょう。 コンパイル時に、静的にクラス型を判定させるには、id 型ではなくクラス名を型としたクラス型のポインタを使ってインスタンスを参照します。 例えば、Point クラス専用の変数は Point 型へのポインタとして宣言します。
#import <stdio.h> #import <objc/Object.h> @interface A : Object - (void)Write; @end @implementation A - (void)Write { printf("A . Write Method\n"); } @end int main() { A * obja = [A new]; [obja Write]; [obja free]; return 0; }
このプログラムは、宣言した A クラスのインスタンスを作成し、A * 型の変数 obja に保存しています。 これまでは id 型にオブジェクトを保存してきましたが、このプログラムでは明示的に A クラス型であることを指定しています。
クラス型のポインタは、メモリ上の実体であるインスタンスのアドレスを保存するために用いられます。 id 型もインスタンスのアドレスを保存するためのものですが、型としてはクラス型を指定したほうが明確です。 通常、Objective-C のクラス型変数は構造体型の変数のように、静的な変数として宣言することは出来ません。 クラス型の変数は必ずポインタ型として扱われるべきなのです。
SmallTalk を源流とする Objective-C 言語では、比較的 id 型が利用されている傾向がありますが、現代のオブジェクト指向言語のスタンダードな記述方法から考えれば、クラス型を明記した変数を使った方が良いでしょう。 id 型を使うのは、alloc や init のような、返す値が多様化されているメソッドなどに限定するべきです。
では、クラス型の変数は、そのクラス型のインスタンスへのポインタしか代入できないのでしょうか。 C 言語の原理を考えれば、所詮はポインタに過ぎないのでそのようなことはありません。