When I use objc runtime function class_addMethod()
to inject an implementation to NSObject's instance selector, it actually inject the implementation to the instance selector AND the class selector:
@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);
Now we just declare the interface of helloWorld
via Objc Category, and invoke the helloWorld
message to both NSObject
instance and class:
// 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];
Although you just injected the helloWorld
implementation to the NSObject instance selector via class_addMethod()
, but both class and instance messages are resolved after injection:
=> 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
After testing, I found that class_addMethod()
adds the implementation to both class and instance selectors only when the target class of class_addMethod()
is NSObject
.
Is it a bug for objc-runtime or Cocoa?
Nope, it's not a bug. It's defined (albeit obscure) behavior of the runtime system.
Just as every instance has an isa
instance variable that points to it's class, every class structure in memory has an isa
member that points to its metaclass. And just as any given class contains metadata about its instances -- including the list of methods that instances respond to -- the class's metaclass contains metadata about the class itself, including the list of methods that the class responds to.
In addition, every class structure has a superclass
member that points to its superclass, which is mirrored in the metaclass hierarchy (i.e., each metaclass's superclass
is another metaclass).
There's one major difference though: the superclass of NSObject
is nil
, while the superclass of the NSObject
metaclass is NSObject
. In other words, NSObject
's metaclass inherits NSObject
's instance methods. As a consequence, Objective-C classes not only respond to their defined class methods, they also respond to NSObject
instance methods.
Confused yet? Greg Parker wrote an excellent blog that includes a very helpful diagram illustrating how this is all wired together:
Hamster Emporium archive - Classes and metaclasses
Edit
Alas, the internets. If the browser you're currently using doesn't display inline PDF documents, here's a link directly to the diagram:
Hamster Emporium archive - class diagram
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With