Why does [NSObject respondsToSelector:@selector(init)] return 1?
objective-c
Solution
Short Answer:
- You can send any `NSObject` instance method (such as `respondsToSelector:` or `init`) to the `NSObject` class, or to any class that inherits from `NSObject`.
- `[NSObject init]` is overridden in CoreFoundation and throws a runtime exception (for binaries linked on OS X 10.6 or later).
Long Answer:
Let's start with your last question:
Furthermore, since `respondsToSelector` is an instance method, why is it even possible to call it in the first place?
`respondsToSelector:` is an instance method of the `NSObject` protocol, to which the `NSObject` class conforms. Now the `NSObject` class (and each its subclasses) is an object and an instance of a subclass of the root class `NSObject`.
This is explained and illustrated in Greg Parker's article [objc explain]: Classes and metaclasses (emphasis added):
More important is the superclass of a metaclass. The metaclass's superclass chain parallels the class's superclass chain, so class methods are inherited in parallel with instance methods. And the root metaclass's superclass is the root class, so each class object responds to the root class's instance methods. In the end, a class object is an instance of (a subclass of) the root class, just like any other object.
This explains why you can send `respondsToSelector:` to the `NSObject` class at all. 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 this message to the `NSString` class. In this case, the result is the same as
NSString *path = [NSString pathWithComponents:@[@"foo", @"bar"];
Now to your initial question:
Why does running `respondsToSelector` on NSObject with the selector "init" return 1 even though running `[NSObject init]` gives a runtime error?
With the same reasoning as above, it must be possible to send the `init` message to any class that is derived from `NSObject`.
Now `NSObject` has a class method `init`, which 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
The comment in NSObject.mm states that `+init` is overridden in CoreFoundation, and indeed, when the exception is thrown, the stack backtrace is
(lldb) bt
* thread #1: tid = 0x6eda, 0x01f69952 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
....
frame #8: 0x0156b9fc libobjc.A.dylib`objc_exception_throw + 323
frame #9: 0x01889931 CoreFoundation`+[NSObject(NSObject) init] + 241
* frame #10: 0x00002a51 foo`main(argc=1, argv=0xbfffee30) + 257 at main.m:25
so this CoreFoundation method is responsible for the exception.
I do not know why the `init` method is overridden in CoreFoundation (instead of throwing an exception directly in `NSObject`), and the replaced method seems not be 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 behaviour has changed between OS releases. If
id x = [NSObject init];
is compiled on OS X 10.5, then it works and returns an `NSObject` class object. This works even if the binary has been compiled and linked on OS X 10.5 and runs on a later release such as OS X 10.9.
This can also be seen from the assembly code of
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 has been linked on OS X 10.6 or later, and throws an exception in that case.
So calling `init` on the class object must have been allowed in earlier OS releases, and CoreFoundation still allows it in "old binaries" for backward compatibility.
Problem
Why does running respondsToSelector on NSObject with the selector "init" return 1 even though running [NSObject init] gives a runtime error? I know that init is an instance method and therefore only runs on instances and not classes. Why does this return an runtime error? ``` if([NSObject respondsToSelector: @selector(init)] == YES ) [NSObject performSelector: @selector(init)]; ``` Furthermore, since respondsToSelector is an instance method, why is it even possible to call it in the first place?