Как получить селектор метода класса?
У меня есть следующий код, где я получаю указатель на метод экземпляра:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface TestClass : NSObject
@end
@implementation TestClass
- (void)someMethod { // This is instance method, it's okay
NSLog(@"Hello from some method!");
}
@end
int main(int argc, const char * argv[]) {
typedef void (*MethodWithoutParams)();
MethodWithoutParams someMethodImplementation =
class_getMethodImplementation([TestClass class], @selector(someMethod));
someMethodImplementation();
return 0;
}
Это работает довольно хорошо. Но если я хочу получить указатель на метод класса, он не работает:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface TestClass : NSObject
@end
@implementation TestClass
+ (void)someMethod { // This is class method, it doesn't work
NSLog(@"Hello from some method!");
}
@end
int main(int argc, const char * argv[]) {
typedef void (*MethodWithoutParams)();
MethodWithoutParams someMethodImplementation =
class_getMethodImplementation([TestClass class], @selector(someMethod));
someMethodImplementation();
return 0;
}
Это не работает, потому что он не может найти реализацию метода.
Я уверен, что он должен работать, потому что он работает, если я получаю реализацию таким образом:
MethodWithoutParams someMethodImplementation =
[TestClass methodForSelector:@selector(someMethod)];
Итак, я заглянул в реализацию NSObject и увидел следующий код:
+ (IMP)methodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return object_getMethodImplementation((id)self, sel);
}
- (IMP)methodForSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return object_getMethodImplementation(self, sel);
}
Также object_getMethodImplementation()
функция имеет следующую реализацию:
IMP object_getMethodImplementation(id obj, SEL name)
{
Class cls = (obj ? obj->getIsa() : nil);
return class_getMethodImplementation(cls, name);
}
Таким образом, реализация одинакова как для поиска метода класса, так и для поиска метода экземпляра.
Но это не работает, и я понятия не имею, почему. Я предполагаю, что любой тип методов (как класс, так и экземпляр) будет расположен в таблице диспетчеризации, и я могу получить указатель на любой из этих методов. Но, как видите, я не могу этого сделать.2 ответа:
Вам нужно запросить метакласс класса. Метод класса-это метод экземпляра в его метаклассе.
Class meta = objc_getMetaClass("TestClass"); SEL sel = @selector(someMethod); typedef void (*MyMethodImplementation)(Class, SEL); MyMethodImplementation someMethodImplementation = (MyMethodImplementation)class_getMethodImplementation(meta, sel); someMethodImplementation([TestClass class], sel);
Обратите внимание, что метод (экземпляр или класс) никогда не бывает "без параметров". Он всегда должен вызываться по крайней мере с приемником (экземпляром или классом) и селектором.
У меня нет абсолютно никакого представления о Objective-C runtime. Но вот моя тирада:
Object_getMethodImplementation для методов класса:
// returns a class method implementation, can't find instance methods class_getMethodImplementation([TestClass class]->getIsa(), @selector(classMethod));
, что эквивалентно
class_getMethodImplementation(objc_getMetaClass("TestClass"), @selector(classMethod));
Object_getMethodImplementation например методы:
// returns an instance method implementation, can't find class methods class_getMethodImplementation((<TestClass instance>)->getIsa(), @selector(someMethod));
Что эквивалентно:
class_getMethodImplementation([TestClass class], @selector(someMethod))
Таким образом, кажется, что
<TestClass instance>->getIsa()
возвращает указатель, равный тому, что возвращает[TestClass class]
. И я думаю, теперь вы знаете, в чем разница междуself
в классе метод иself
в методе экземпляра.Поэтому вы должны использовать мета-класс, как предлагает @KenThomases.