Как получить селектор метода класса?


У меня есть следующий код, где я получаю указатель на метод экземпляра:

#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 2

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.