Objective-C コンパイラは、メソッドを特定する名前をコンパイル時に内部表現に変換します。 この、メソッドの内部表現をセレクタと呼び、メッセージの送受信の裏側では、このセレクタがやり取りされています。 メソッドを特定するための内部表現についてはコンパイラに依存する問題であり、開発者が知るべき範囲ではありません。 開発者にとって重要なのは、このセレクタを SEL 型として扱うことができるという事実です。
メソッドがどのようなデータに変換され、どのように識別されているかは問題ではありません。 しかし、Objective-C はこの内部表現を SEL 型の変数として扱うことを保障しています。 つまり、SEL 型の変数には、メソッド名を識別するためにコンパイラが割り当てた特殊なコードを保存することができるということになります。
メソッドを特定するセレクタは @selector コンパイラディレクティブを用いて取得することができます。
@selector ( method )
method には、セレクタを取得したいメソッドの名前を指定します。 指定したメソッドの名前が存在するかどうかは、メソッドを呼び出すときに、実行時に判定されるため、コンパイル時には評価されないでしょう。
では、取得したセレクタの値を SEL 型の変数に保存したとして、これをどのように利用することができるのでしょうか。 セレクタがメソッドを特定するという性質がある以上、やはり最大の利用方法は、関数へのポインタのように、動的にメソッドを特定する方法でしょう。 セレクタからメソッドを呼び出す機能を提供するのはルートクラスです。
Object クラスには、SEL 型の値を受け取る perform メソッドが宣言されています。 このメソッドは、引数で受け取ったセレクタが特定するメソッドを実行します。
- perform:(SEL)aSel;
- perform:(SEL)aSel with:anObject;
- perform:(SEL)aSel with:anObject1 with:anObject2;
aSel に、呼び出すメソッドのセレクタを指定します。 anObject、anObject1、anObject2 には、メソッドに渡す引数を指定することができます。
#import <stdio.h> #import <objc/Object.h> @interface Test : Object - (void)Write; @end @implementation Test - (void)Write { printf("I am the bone of my sword.\n"); } @end int main() { id obj; SEL method; obj = [Test new]; method = @selector(Write); [obj perform:method]; return 0; }
このプログラムでは、Write メソッドを表すセレクタを SEL 型の変数 method に保存しています。 そして、Test クラスのインスタンス obj に perform メッセージを method を引数として送信します。 perform メソッドは、与えられたセレクタから実行するべきメソッドを割り出し、実行します。 この性質を上手く利用すれば、実行時に呼び出すべきメソッドを状況に応じて切り替えるようなプログラムを実現することができます。