Objective-C は late binding な言語。これって、メソッドに返事ができるかどうかは、評価するまで分からない、ってことを意味する。それならば、評価をしてみよう!てなわけで、プログラム中でのメソッドの評価の仕方の話だ。
セレクタを評価するためのメソッドとして、NSObject に instancesRespondToSelector: が、NSObject protocol として respondsToSelector: が定義されている。
Foundation/NSObject.h
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
- (BOOL)respondsToSelector:(SEL)aSelector;
さて、この 2 つはどうやって使い分けるのか?まず、instanceRespondToSelector: から。こいつは、そのクラスのインスタンスがセレクタを受け付けるかどうかを判別する。ということは、インスタンスメソッドは判別できるけど、クラスメソッドには反応しないんだ。次の例を参照。
(sample)
// It returns YES
[NSString instancesRespondToSelector:@selector(length)];
// It returns NO
[NSString instancesRespondToSelector:@selector(string)];
上の例では、NSString を調べている。length は NSString のインスタンスメソッドだから、YES を返す。それに対して、string はクラスメソッドになるから、 NO を返すんだ。
続いて respondsToSelector:。こいつは、このメッセージの受け手がそのセレクタに反応できるかどうかを評価するんだ。たとえば、NSString のインスタンスに対して投げてやると次のような感じ。
(sample)
NSString* str = [NSString string];
// It returns YES
[str respondsToSelector:@selector(length)];
// It returns NO
[str respondsToSelector:@selector(string)];
インスタンスである str は、length には反応するが string には反応できない。では、これを NSString に対して投げてやる。
(sample)
// It returns NO
[NSString respondsToSelector:@selector(length)];
// It returns YES
[NSString respondsToSelector:@selector(string)];
という風に、length には NO だが、string には YES を返す。クラスである NSString はクラスメソッドに反応できるからだ。
というわけで、どちらを使うかはお好み次第で。
次は、メソッドの返り値を調べてみよう。これには NSMethodSignature の methodReturnType と methodReturnLength を使う。
Foundation/NSMethodSignature.h
- (const char *)methodReturnType;
- (unsigned)methodReturnLength;
methodRetrunType は、メソッドの返り値の型を Objective-C encoding で返すんだ。だから、結果を比較するには @encode を使わないといけないところを注意。
(sample)
NSString* string = [NSString string];
NSMethodSignature* signature;
// For 'length'
signature = [string methodSignatureForSelector:@selector(length)];
if (strcmp([signature methodReturnType], @encode(unsigned int)) == 0) {
...
}
// For 'copy'
signature = [string methodSignatureForSelector:@selector(copy)];
if (strcmp([signature methodReturnType], @encode(id)) == 0) {
...
}
// For 'pathComponents'
signature = [string methodSignatureForSelector:@selector(pathComponents)];
if (strcmp([signature methodReturnType], @encode(id)) == 0) {
...
}
こんな感じで使える。methodReturnType の結果を strcmp() で比較しないといけないところに注意。また、ヘッダでは NSArray* を返している pathComponents は、id 型として認識しないといけない。返り値型が NSArray* を取る、という情報は、コンパイルをした時点で id 型に丸められてしまうようだ。