Objective-C において、下位互換のあるコードを書いていると
特定のメソッドがある場合はそれを使い、そうでない場合は違う方法で
という書き方をよくする。
その中で、このインスタンスはこのインスタンスメソッドが呼び出せるかというのはよく使っていて、それは
@interface NSObject
- (BOOL)respondsToSelector:(SEL)aSelector;
@end
を使えばよいのだけど、
でもこれ NSObject のインスタンスメソッドなので、
じゃあクラスメソッドが呼び出せるかどうかはどうしたらいいのよってことで調べてみた。
上記と同じように NSObject にあるだろうと思ってそれらしいメソッドを探してみたけところ、それっぽいのでクラスメソッドとして提供されているのは
@interface NSObject
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
@end
というものがあった。
しかしメソッド名から判断するに、インスタンスはこのセレクタに応答出来るかってメソッドなのでクラスメソッドには使えなさそう。
ということで試行錯誤した結果が以下
#import <Foundation/Foundation.h>
int main(void) {
@autoreleasepool {
NSLog(@"-respondsToSelector: Class Method : %@", @([[NSObject class] respondsToSelector:@selector(new)]));
NSLog(@"-respondsToSelector: Instance Method : %@", @([[NSObject new] respondsToSelector:@selector(description)]));
NSLog(@"+instancesRespondToSelector: Class Method : %@", @([NSObject instancesRespondToSelector:@selector(new)]));
NSLog(@"+instancesRespondToSelector: Instance Method : %@", @([NSObject instancesRespondToSelector:@selector(description)]));
}
return 0;
}
これを実行すると
-respondsToSelector: Class Method : 1
-respondsToSelector: Instance Method : 1
+instancesRespondToSelector: Class Method : 0
+instancesRespondToSelector: Instance Method : 1
となる。
##まとめ##
クラスメソッドの存在もインスタンスメソッドの存在も respondsToSelector: で確認出来る。
そのセレクタを渡すレシーバに、インスタンスを渡すか Class を渡すかで切り替えられる。
##おまけ##
実は、調査の過程でNSObjectのI/Fには書いていなかったが上記テストコードの実装ミスから見つけたものがあって、
respondsToSelector: をクラスメソッドとしても呼び出せ、そうすることでクラスメソッドの存在が確認できた。
NSLog(@"+respondsToSelector: Class Method : %@", @([NSObject respondsToSelector:@selector(new)]));
こんな感じ。これは YES が返る。
上述のテストコードで respondsToSelector: がクラスメソッドとして存在するか、インスタンスメソッドとして存在するかを確認したところ、どちらも YES となった。