Почему функция выполнения objc "class addMethod ()" добавляет реализацию как экземпляр и метод класса, когда целевой класс является "NSObject"?

когда я использую функцию выполнения objc class_addMethod() чтобы внедрить реализацию в селектор экземпляра NSObject, он фактически внедряет реализацию в селектор экземпляра и селектор класса:

@implementation HelloWorldClass
- (void) helloWorld{
    NSLog(@"hello world from instance method -helloWorld");
}
@end

// ====
// Do method injection when the application did finish launching.
Class sourceClass = objc_getClass("HelloWorldClass");
Class targetClass = objc_getClass("NSObject");
SEL helloWorldSelector = @selector(helloWorld);

Method method = class_getInstanceMethod(sourceClass, helloWorldSelector);
IMP imp = method_getImplementation(method);
const char *methodTypeEncoding = method_getTypeEncoding(method);

class_addMethod(targetClass, helloWorldSelector, imp, methodTypeEncoding);

теперь мы просто объявляем интерфейс helloWorld через категорию Objc и вызовите helloWorld сообщения NSObject экземпляр и класс:

// Declare the interface for `helloWorld
@interface NSObject (HelloWorld)
+ (void) helloWorld;
- (void) helloWorld;
@end


// Send the `helloWorld` message to NSObject class
NSLog(@"Send the `helloWorld` message to NSObject class");
[NSObject helloWorld];

// Send the `helloWorld` message to NSObject instance
NSLog(@"Send the `helloWorld` message to NSObject instance");
[[NSObject new] helloWorld];

хотя вы только что ввели helloWorld реализация экземпляра NSObject селектор через class_addMethod(), но сообщения класса и экземпляра разрешаются после инъекции:

=> Send the `helloWorld` message to NSObject class
=> hello world from instance method -helloWorld
=> Send the `helloWorld` message to NSObject instance
=> hello world from instance method -helloWorld

после тестирования, я обнаружил, что class_addMethod() добавляет реализацию к селекторам классов и экземпляров только тогда, когда целевой класс class_addMethod() is NSObject.

это ошибка для objc-runtime или Cocoa?

1 ответов


нет, это не ошибка. Это определенное (хотя и неясное) поведение системы выполнения.

так как каждый экземпляр имеет isa переменная экземпляра, указывающая на класс, каждая структура класса в памяти имеет isa член, который указывает на его метакласс. И так же, как любой данный класс содержит метаданные о своих экземплярах, включая список методов, на которые отвечают экземпляры, метакласс класса содержит метаданные о самом классе, включая список методов, которые класс реагирует.

кроме того, каждая структура класса имеет superclass элемент, указывающий на его суперкласс, который отражается в иерархии метаклассов (т. е. superclass еще один метакласс).

есть одно важное отличие: суперкласс NSObject is nil, а суперкласс NSObject метакласс-это NSObject. Другими словами,NSObjectметакласс наследует NSObject'ы метод экземпляра. Как следствие, классы Objective-C не только отвечают на их определенные методы класса, они также отвечают на NSObject методы экземпляра.

еще смущает? Грег Паркер написал отличный блог, который включает в себя очень полезную диаграмму, иллюстрирующую, как все это связано вместе:

архив Hamster Emporium-классы и метаклассы

редактировать

увы, в интернетах. Если браузер вы в настоящее время использование не отображает встроенные PDF-документы, вот ссылка непосредственно на диаграмму:

Hamster Emporium архив-диаграмма классов