- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
为什么在带有选择器“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/
询问 unrelated question我有这样的代码: public boolean equals(Object obj) { if (this == obj) retur
在我之前的一个问题中 js: Multiple return in Ternary Operator我询问了有关使用三元运算符返回多个参数的问题。但是现在参数IsActveUser boolean(t
假设我有一个带有 return 的 if 语句。从效率的角度来看,我应该使用 if(A > B): return A+1 return A-1 或 if(A > B): return
例如考虑以下代码: int main(int argc,char *argv[]) { int *p,*q; p = (int *)malloc(sizeof(int)*10); q
PyCharm 对这段代码发出警告,说最后一个返回是不可访问的: def foo(): with open(...): return 1 return 0 如果 ope
我想实现这样的目标: 如果在返回 Json 的方法中抛出异常,则返回 new Json(new { success = false, error = "unknown"}); 但如果方法返回 View
它是多余的,但我正在学习 JS,我想知道它是如何工作的。 直接从模块返回函数 let func1 = function () { let test = function () {
我不明白我应该使用什么。我有两页 - intro.jsp(1) 和 booksList.jsp(2)。我为每一页创建了一个 Controller 类。第一页有打开第二页的按钮:
我最近在 Joomla 组件(Kunena,更准确地说是 Kunena)中看到这段代码,那么使用 $this->return VS 简单的 return 语句有什么区别. 我已经用谷歌搜索了代码,但没
我的类实现了 IEnumerable。并且可以编译这两种方式来编写 GetEnumerator 方法: public IEnumerator GetEnumerator() { yield r
我只是在编码,我想到了一个简单的想法(显然是问题),如果我有一个像这样的函数: int fun1(int p){ return(p); } 我有一个这样的函数: int fun1(int p){
这个问题在这里已经有了答案: What does the comma operator do in JavaScript? (5 个答案) 关闭 9 年前。 function makeArray
假设我写了一个 for 循环,它将输出所有数字 1 到 x: x=4 for number in xrange(1,x+1): print number, #Output: 1 2 3 4 现
我最近在这个 Apache Axis tutorial example. 中看到了下面的一段代码 int main() { int status = AXIS2_SUCCESS; ax
function a(){ return{ bb:"a" } } and function a(){ return { bb:"a" } } 这两个代码有什么区别吗,如果有请
function a() { return 1; } function b() { return(1); } 我在 Chrome 的控制台中测试了上面的代码,都返回了 1。 function c()
考虑这三个函数: def my_func1(): print "Hello World" return None def my_func2(): print "Hello World"
这可能是一个愚蠢的问题,但我正在努力,如果有一种简明的方法来测试函数的返回结果,如果它不满足条件,则返回该值(即,传递它)。。现在来回答一个可能的问题,是的,我正在寻找的类似于例外提供的东西。然而,作
我正在测试一个函数,并尝试使用 return 来做什么,并在 PowerShell 5.1 和 PwSh 7.1 中偶然发现了一个奇怪的问题,即 return cmdlet似乎不适合在团体中工作: P
这个问题已经有答案了: Return in generator together with yield (2 个回答) Why can't I use yield with return? (5 个回
我是一名优秀的程序员,十分优秀!