gpt4 book ai didi

objective-c - 为什么 [NSObject respondsToSelector :@selector(init)] return 1?

转载 作者:太空狗 更新时间:2023-10-30 03:35:58 24 4
gpt4 key购买 nike

为什么在带有选择器“init”的 NSObject 上运行 respondsToSelector 会返回 1,即使运行 [NSObject init] 会出现运行时错误?我知道 init 是一个实例方法,因此只在实例而不是类上运行。为什么这会返回运行时错误?

if([NSObject respondsToSelector: @selector(init)] == YES )
[NSObject performSelector: @selector(init)];

此外,既然 respondsToSelector 是一个实例方法,为什么甚至可以首先调用它呢?

最佳答案

简答:

  • 您可以发送任何 NSObject 实例方法(例如respondsToSelector:init) 到 NSObject class,或继承自 NSObject 的任何类。
  • [NSObject init] 在 CoreFoundation 中被覆盖并抛出运行时异常(对于在 OS X 10.6 或更高版本上链接的二进制文件)。

长答案:

让我们从您的最后一个问题开始:

Furthermore, since respondsToSelector is an instance method, why is it even possible to call it in the first place?

respondsToSelector:NSObject 协议(protocol)的一个实例方法,NSObject 类符合。现在 NSObject 类(及其每个子类) 是一个对象,也是根类 NSObject 的子类的实例。

Greg Parker 的文章对此进行了解释和说明 [objc explain]: Classes and metaclasses(强调):

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.

这解释了为什么您可以将 respondsToSelector: 发送到 NSObject 类。如果存在具有给定选择器的类方法,则返回值为 YES

这是另一个例子:

NSString *path = [NSString performSelector:@selector(pathWithComponents:) withObject:@[@"foo", @"bar"]];

performSelector:withObject:NSObject 的一个实例方法,你可以发送这个消息到 NSString 。在这种情况下,结果与

NSString *path = [NSString pathWithComponents:@[@"foo", @"bar"];

现在回答您最初的问题:

Why does running respondsToSelector on NSObject with the selector "init" return 1 even though running [NSObject init] gives a runtime error?

同理,发送init消息一定是可行的派生自 NSObject 的类。

现在 NSObject 有一个类方法 init,它被记录为抛出一个运行时异常,见http://opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm :

// Replaced by CF (throws an NSException)
+ (id)init {
return (id)self;
}

这解释了原因

[NSObject respondsToSelector:@selector(init)] == YES

NSObject.mm 中的注释指出 +init 在 CoreFoundation 中被覆盖,事实上,抛出异常时,栈回溯为

(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

所以这个 CoreFoundation 方法是导致异常的原因。


我不知道 为什么 init 方法在 CoreFoundation 中被覆盖(而不是直接在 NSObject 中抛出异常),并替换方法似乎不是开源存储库的一部分 http://opensource.apple.com/source/CF/CF-855.14/(至少我在那里找不到它)。

但一个有趣的点可能是观察到的行为在操作系统版本之间发生了变化。如果

 id x = [NSObject init];

在 OS X 10.5 上编译,然后它可以工作并返回一个 NSObject 类对象。即使二进制文件已在 OS X 10.5 上编译和链接并在更高版本上运行,这仍然有效作为 OS X 10.9。

这个从

的汇编代码也可以看出
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()(来自 http://www.opensource.apple.com/source/CF/CF-476.14/CFPriv.h)检查二进制文件是否已链接到 OS X 10.6 或更高版本,并抛出异常在这种情况下。

因此在早期的操作系统版本中必须允许在类对象上调用 init,为了向后兼容,CoreFoundation 仍然允许在“旧二进制文件”中使用它。

关于objective-c - 为什么 [NSObject respondsToSelector :@selector(init)] return 1?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23692980/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com