Why the objc runtime function `class_addMethod()` adds the implementation as instance and class method when the target class is `NSObject`?

objective-c, objective-c-runtime

Solution

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

Problem

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?

Original source