Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
理解Objective-C中选择器的唯一性_Objective C_Selector_Objective C Runtime - Fatal编程技术网

理解Objective-C中选择器的唯一性

理解Objective-C中选择器的唯一性,objective-c,selector,objective-c-runtime,Objective C,Selector,Objective C Runtime,我在理解苹果指南中描述的“选择器”的部分功能时遇到问题。我已将我感到困惑的部分加粗: 在Objective-C中,选择器有两种含义。可供参考 在源代码消息中使用的方法的名称 指向一个对象但它也引用了 编译源代码时替换名称。compiled 选择器为SEL类型具有相同名称的所有方法都具有 相同的选择器。您可以使用选择器调用 对象这为实现 Cocoa中的目标动作设计模式 方法和选择器为提高效率,不使用ASCII全名作为 编译代码中的方法选择器。相反,编译器编写每个 方法名称,然后将该名称与唯一标识符

我在理解苹果指南中描述的“选择器”的部分功能时遇到问题。我已将我感到困惑的部分加粗:

在Objective-C中,选择器有两种含义。可供参考 在源代码消息中使用的方法的名称 指向一个对象但它也引用了 编译源代码时替换名称。compiled 选择器为SEL类型具有相同名称的所有方法都具有 相同的选择器。您可以使用选择器调用 对象这为实现 Cocoa中的目标动作设计模式

方法和选择器为提高效率,不使用ASCII全名作为 编译代码中的方法选择器。相反,编译器编写每个 方法名称,然后将该名称与唯一标识符配对 表示运行时的方法运行时系统确保 每个标识符都是唯一的:没有两个选择器是相同的,而且都是相同的 具有相同名称的方法具有相同的选择器。

有人能解释这些吗?此外,如果不同的类具有相同名称的方法,它们是否也具有相同的选择器

不过,它还引用了在编译源代码时替换名称的唯一标识符。。。具有相同名称的所有方法都具有相同的选择器

为此,我参考了一个优秀的选择器:

对于具有相同名称和参数的所有方法,选择器都是相同的—无论定义它们的对象是什么,这些对象是否在类层次结构中相关,或者实际上彼此无关。在运行时,Objective-C转到类并直接询问它,“您是否响应此选择器?”,如果响应,则调用结果函数指针

运行时系统确保每个标识符都是唯一的:没有两个选择器是相同的,并且具有相同名称的所有方法都具有相同的选择器


以一种糟糕的方式,这是有道理的。如果方法A和方法B具有完全相同的名称和参数,那么将它们的选择器存储为一个查找并查询接收者,而不是在运行时在两个名称基本相同的选择器之间进行选择,不是更有效吗?

是的。类不共享选择器

我可以从源代码
objc sel.mm
中给出一个示例,但是当您使用
sel\u registerUid()
(在
@selector()
的后台使用)时, 它将输入字符串复制到一个内部缓冲区(如果该字符串以前没有注册过),所有未来的SEL都指向该缓冲区


这样做的目的是减少内存使用,简化消息转发。

查看SEL类型,您不必定义此选择器来自哪个类,只需给它一个方法名,例如:

SEL animationSelector = @selector(addAnimation:forKey:); 

例如,你可以想象它是一个街道名称。许多城市可以有相同的街道名称,但是没有城市的街道名称是毫无价值的。对于选择器也是一样,您可以定义选择器,而无需在其中添加对象。但是,如果没有安装类,它是完全没有价值的。

所有选择器都是唯一的——在编译时,以及在运行时通过
sel\getUid()
或首选的
sel\u registerName()
(后者主要是首选的,前者仍存在于历史原因)——表示速度

背景故事:要调用一个方法,运行时需要一个选择器来标识要调用的对象和要调用的对象。这就是为什么Objective-C中的每个方法调用都有两个参数:显而易见且众所周知的
self
和不可见且隐含的参数
\u cmd
\u cmd
是当前执行的方法的SEL。也就是说,您可以将此代码粘贴到任何方法中,以查看当前正在执行的方法的名称(选择器):

NSLog(@"%@", NSStringFromSelector(_cmd));
请注意,
\u cmd
不是全局命令;这确实是对你的方法的一个论证。见下文

通过对选择器进行uniquing,所有基于选择器的操作都是使用指针相等性测试来实现的,而不是字符串处理或任何指针取消引用

特别是,每次进行方法调用时:

[someObject doSomething: toThis withOptions: flags]; // calls SEL doSomething:withOptions:
编译器生成以下代码(或一个非常密切相关的变体):

objc_msgSend()
要做的第一件事就是检查
someObject
是否为nil,如果为nil,则检查是否短路(nil消息)。下一步(忽略标记的指针)是在
someObject
s类中查找选择器(实际上是
isa
指针),找到实现,并调用它(使用尾部调用优化)

找到实现事情必须是快速的,要使它真正快速,您希望找到方法实现的关键是尽可能快速和稳定。要做到这一点,您需要密钥直接可用,并且是流程的全局唯一密钥

因此,选择器是唯一的

它还可以节省内存,这是一个非常好的好处,但是如果Messeng可以比现在快2倍,那么messenger将使用更多的内存(但不是10倍的速度,甚至不是2倍的内存速度,虽然速度至关重要,但内存的使用也很关键)


如果你真的想深入研究
objc\u msgSend()
的工作原理,我写了一篇文章。请注意,在公开标记指针、作为实现的块和ARC之前,它已经稍微过时了。我应该更新这些文章。

很多时候,我想知道您是否在秘密地使用ObjC运行时,这就是其中之一+感谢您的帮助和可爱的回答。@RichardJ.Ross:
!否=是
;)虽然细节是正确的,但结论忽略了最重要的一点
objc_msgSend(someObject, @selector(doSomething:withOptions:), toThis, flags);