メソッドのポインタ


メソッドを関数として呼び出す

実は、Objective-C のメソッドの実態は C 言語の関数と同じです。 普段は隠蔽されていますが、メソッドとは最初の引数に自分のクラスを参照するオブジェクトを受け取る関数なのです。

メソッドの実体が関数であるという事実は、C 言語との親和性が極めて高いことを意味してます。 ピュアな C 言語で記述されたライブラリから Objective-C のメソッドを呼び出したり、オブジェクトを利用することも無理な話ではないのです。

Objective-C のメソッドは、常に IMP 型であると定義されます。 IMP 型は、ヘッダファイルで次のように定義されています。

typedef id (*IMP)(id, SEL, ...);

この定義からも分かるように、Objective-C で宣言されたすべてのメソッドは、暗黙的に id 型と SEL 型の引数を持ちます。 第一引数は、メソッドを呼び出したオブジェクトを表す変数 self です。 第二引数は、このメソッドのセレクタを表す変数 _cmd です。 すべてのメソッドには、これらの隠された引数が必ず存在します。 その後は、メソッドの宣言に応じて引数が決定されるでしょう。

C 言語や、何らかの理由で Objectiver-C から、メソッドを関数として呼び出す必要がある場合、メソッドを参照する関数へのポインタを取得すればよいのです。 メソッドを参照する IMG 型のポインタは Object クラスの instanceMethodFor クラスメソッド、または methodFor インスタンスメソッドから得ることができます。

+ (IMP)instanceMethodFor:(SEL)aSel;

- (IMP)methodFor:(SEL)aSel;

aSel には、対象メソッドの IMP、すなわち関数へのポインタを取得したいセレクタを指定します。 メソッドは、aSel に指定されたセレクタが特定するメソッドのポインタを返します。

ポインタを取得することができれば、C 言語からでもインスタンスメソッドを呼び出すことができるようになります。 関数のポインタとして直接呼び出すため、メッセージ通信よりも高速になるという点も特徴でしょう。

#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;
	IMP func;

	obj = [Test new];
	method = @selector(Write);
	func = [Test instanceMethodFor:method];
	func(obj , method);

	return 0;
}

このプログラムは、Test クラスのインスタンスメソッド Write へのポインタを取得し、IMP 型の変数 func に保存しています。 [Test instanceMethodFor:method] というメッセージ式の部分は、[obj methodFor:method] としても意味は同じです。 このメッセージ式が返した IMP 型のメソッドのポインタを使って、直接メソッドを呼び出しています。

画像処理やマルチメディアなど、ループ処理の高速化などが必要な場合、メッセージ通信やセレクタ通信は負担が大きくなります。 速度にこだわるプログラムであれば、必要に応じて IMP を取得し、ポインタからメソッドを呼び出しても良いでしょう。



前のページへ戻る次のページへ