2015-02-21 5 views
2

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

#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() Также функция has следующая реализация:

IMP object_getMethodImplementation(id obj, SEL name) 
{ 
    Class cls = (obj ? obj->getIsa() : nil); 
    return class_getMethodImplementation(cls, name); 
} 

Таким образом, реализация одинакова для поиска метода класса и поиска метода экземпляра.

Но это не сработает, и я понятия не имею, почему. Я предполагаю, что любой тип методов (как класса, так и экземпляра) будет размещен в таблице рассылки, и я могу получить указатель на любой из этих методов. Но, как вы видите, я не могу этого сделать.

+0

Зачем вам это нужно? – Wain

+2

@Wain Я просто хочу знать, как работает Objective-C Runtime. К сожалению, официальная документация не описывает этот вопрос настолько глубоко. –

+1

Это не одна и та же реализация. 'self' в методе экземпляра и' self' в методе класса разные. Таким образом, он вызывает 'object_getMethodImplementation' с разными' self '. – mostruash

ответ

5

Вам необходимо запросить метакласс класса. Метод класса - это метод экземпляра в его метаклассе.

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); 

Примечание. Метод (экземпляр или класс) никогда не «без параметров». Его всегда следует вызывать, по крайней мере, с приемником (экземпляр или класс) и селектором.

+0

Большое вам спасибо за помощь! Да, я знаю, что я должен передать по меньшей мере два этих параметра методу. Я просто упростил код для тестирования. –

+0

или, что эквивалентно, class meta = object_getClass ([Класс TestClass]); ', чтобы показать, что процесс на самом деле то же самое с методами экземпляра – newacct

1

У меня нет абсолютно никакого представления о времени выполнения Objective-C. Но вот моя тирада:

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.