11

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?

Xaree Lee
  • 3,188
  • 3
  • 34
  • 55

1 Answers1

9

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

jlehr
  • 15,557
  • 5
  • 43
  • 45
  • Is this possible to add implementations ONLY to the instance method list of `NSObject`? Is there any benefit to inject to both `NSObject` class and instance methods when using `class_addMethod()`? – Xaree Lee Nov 30 '13 at 15:28
  • 1
    Your code *is* adding implementations only to the instance method list, but `NSObject`'s metaclass inherits all of its instance methods; the behavior you described is a consequence of that. To the best of my knowledge, adding the same method as a class method wouldn't make any practical difference. You could try pinging [Greg Parker](http://stackoverflow.com/users/881456/greg-parker) for a more definitive answer though. – jlehr Nov 30 '13 at 15:40
  • Really thanks for your answer and comment. But I still don't understand why `NSObject`'s metaclass should inherit all of its instance methods. Hope [Greg Parker](http://stackoverflow.com/users/881456/greg-parker) could answer that. :) – Xaree Lee Nov 30 '13 at 16:18
  • I'm not sure if your question is an existential or a practical one. Do you mean 'why' in the sense of "Why did the designers of the language make that particular choice?" – jlehr Nov 30 '13 at 16:22
  • YES, I wonder "Why did the designers of the language make that particular choice". – Xaree Lee Nov 30 '13 at 16:37
  • 1
    In order for an Objective-C class to be the target of messages, it also has to be capable of properly performing instance behavior. Therefore the method list in its metaclass has to have not only class methods, but also the instance methods that define fundamental object behaviors such as `respondsToSelector:`, etc. – jlehr Nov 30 '13 at 17:03
  • @李岡諭 The reason is that without that ou would have mass of duplicate code. Take into account: 1. A root object does not have an (outer) state. So potentially every instance method can run on a class object. 2. Many of the capabilities of an instance object has to be build in to a class object, too: Messaging, introspection, … Why should one write that code twice? – Amin Negm-Awad Mar 18 '14 at 15:18