変数宣言などでクラス名を指定し、そのクラス型の変数を用意することができました。 しかし、クラスメソッドを呼び出すために、メッセージ式で指定したクラス名は、実はクラスの型を表しているのではありません。 メッセージ式で指定するのは、常にメッセージを送信する先のオブジェクトです。 つまり、メッセージ式で指定していたクラス名は、オブジェクトなのです。
クラスがインスタンスを生成するように、正しくコンパイルされたクラスは、自らの情報を表すクラスオブジェクトを保有しています。 このクラスオブジェクトは Class 型として、変数に保存することも可能です。 クラスオブジェクトが存在しない値は Nil という定数で表現されます。 通常、Nil は NULL と同様に 0 を表します。
クラスオブジェクトを取得するには、インスタンスが存在する場合は、Object クラスの class インスタンスメソッドの戻り値から取得します。 インスタンスが存在しない状態で、クラス名が判明している場合、クラス名を直接指定してクラスオブジェクトを取得することもできます。 通常、クラス名は方の名前として認識されますが、メッセージ式のメッセージ送信先オブジェクトの指定でクラス名を指定した場合に限って、このクラスのクラスオブジェクトとして認識されます。 メッセージ式で、クラス名で指定したクラスオブジェクトを返させるには、クラスオブジェクトに class メッセージを送信します。 このメソッドは次のように宣言されています。
- (Class)class;
このメソッドが返した値が Class 型のオブジェクトです。 クラスオブジェクトは変数に保存することができるため、クラスメソッドを呼び出すメッセージ式も、変数に保存されているオブジェクトによって結果が異なるような多様性を導入することができます。
#import <stdio.h> #import <objc/Object.h> @interface Test : Object + (void)Write; - (id)init; @end @implementation Test + (void)Write { printf("I love you... so please do not love me.\n"); } - (id)init { printf("You can be whatever.\n"); return [super init]; } @end int main() { Class testClass = [Test class]; [testClass Write]; [[testClass new] free]; [testClass free]; return 0; }
このプログラムの [Test class] の Test は、実はクラス型ではなくクラスオブジェクトをメッセージ式に指定してます。 メッセージ式は Test クラスのクラスオブジェクトを返し、testClass 変数にオブジェクトを保存しています。 以降、この testClass 変数は、メッセージ式で Test クラスオブジェクトと同じ意味を持ちます。
この威力は、その後、Write クラスメソッドを実行するために [testClass Write] メッセージ式で実現できるという部分で確認できるでしょう。 クラスオブジェクトを指す変数から Write メッセージを送信しているため、この呼び出しは極めて動的な呼び出しになります。 もちろん、alloc や new メッセージを送信して、クラスオブジェクトからインスタンスを作成することができます。 変数を入れ替えるだけで、呼び出すクラスメソッドや生成するインスタンスを変更できるため、柔軟なプログラムを実現するために利用することができます。