Short answer:
- You can send any method of an
NSObject instance (e.g. respondsToSelector: or init ) to the NSObject class or any class that inherits from NSObject . [NSObject init] overridden in CoreFoundation and throws a runtime exception (for binaries associated with OS X 10.6 or later).
Long answer:
Start with the last question:
Also, since respondsToSelector is an instance method, why can it even be called?
respondsToSelector: is the method of the NSObject protocol NSObject to which the NSObject class corresponds. Now the NSObject class (and each of its subclasses) is an object and an instance of a subclass of the NSObject root class.
This is explained and illustrated in an article by Greg Parker [objc explain]: Classes and metaclasses (emphasis added):
More important is the superclass of the metaclass. The chain of the superclass of the metaclass is parallel to the class of the superclass of the class, so the methods of the class are inherited in parallel with the methods of the instance. And the superclass of the metaclass class is the root class, so every class object responds to instance methods of the root class . After all, a class object is an instance (subclass) of the root class, like any other object.
This explains why you can send respondsToSelector: to the NSObject class NSObject general. The return value is YES if there is a class method with the given selector.
Here is another example:
NSString *path = [NSString performSelector:@selector(pathWithComponents:) withObject:@[@"foo", @"bar"]];
performSelector:withObject: is an instance method of NSObject , and you can send its message to the NSString class. In this case, the result will be the same as
NSString *path = [NSString pathWithComponents:@[@"foo", @"bar"];
Now to your original question:
Why is respondsToSelector running on NSObject with the "init" return 1 selector running even if running [NSObject init] gives a runtime error?
With the same considerations as above, it should be possible to send an init message to any class derived from NSObject .
Now NSObject has an init class method that is documented to throw an exception at runtime, see http://opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm :
// Replaced by CF (throws an NSException) + (id)init { return (id)self; }
This explains why
[NSObject respondsToSelector:@selector(init)] == YES
A comment in NSObject.mm states that +init overridden in CoreFoundation, and indeed, when an exception is thrown, the stack backtracks
(lldb) bt * thread
therefore, this CoreFoundation method is responsible for the exception.
I don't know why the init method is overridden in CoreFoundation (instead of throwing an exception directly in NSObject ), and the replaced method does not seem to be part of the open source repository http://opensource.apple.com/source/CF/ CF-855.14 / (at least I could not find it there).
But one interesting point may be that the observed behavior has changed between OS versions. If a
id x = [NSObject init];
compiled on OS X 10.5, then it works and returns an object of class NSObject . This works even if the binary has been compiled and linked to OS X 10.5 and runs in a later release, such as OS X 10.9.
This is also seen in the build code.
CoreFoundation`+[NSObject(NSObject) init]: 0x1019d8ad0: pushq %rbp 0x1019d8ad1: movq %rsp, %rbp 0x1019d8ad4: pushq %rbx 0x1019d8ad5: pushq %rax 0x1019d8ad6: movq %rdi, %rbx 0x1019d8ad9: movl $0x6, %edi 0x1019d8ade: callq 0x1018dfcc0 ; _CFExecutableLinkedOnOrAfter 0x1019d8ae3: testb %al, %al 0x1019d8ae5: jne 0x1019d8af1 ; +[NSObject(NSObject) init] + 33 0x1019d8ae7: movq %rbx, %rax 0x1019d8aea: addq $0x8, %rsp 0x1019d8aee: popq %rbx 0x1019d8aef: popq %rbp 0x1019d8af0: ret 0x1019d8af1: movq %rbx, %rdi 0x1019d8af4: callq 0x101a19cee ; symbol stub for: class_getName 0x1019d8af9: leaq 0x9fe80(%rip), %rcx ; kCFAllocatorSystemDefault 0x1019d8b00: movq (%rcx), %rdi 0x1019d8b03: leaq 0xc5a66(%rip), %rdx ; @"*** +[%s<%p> init]: cannot init a class object." ... 0x1019d8b72: callq *0x9e658(%rip) ; (void *)0x00000001016b9fc0: objc_msgSend 0x1019d8b78: movq %rax, %rdi 0x1019d8b7b: callq 0x101a19d4e ; symbol stub for: objc_exception_throw
_CFExecutableLinkedOnOrAfter() (from http://www.opensource.apple.com/source/CF/CF-476.14/CFPriv.h ) checks if the binary connection is connected to OS X 10.6 or later, and throws an exception in this case.
Therefore, the init call in the class object must be resolved in earlier versions of the OS, and CoreFoundation still allows it to be used in "old binaries" for backward compatibility.