理解Objective-C中选择器的唯一性
我在理解苹果指南中描述的“选择器”的部分功能时遇到问题。我已将我感到困惑的部分加粗: 在Objective-C中,选择器有两种含义。可供参考 在源代码消息中使用的方法的名称 指向一个对象但它也引用了 编译源代码时替换名称。compiled 选择器为SEL类型具有相同名称的所有方法都具有 相同的选择器。您可以使用选择器调用 对象这为实现 Cocoa中的目标动作设计模式 方法和选择器为提高效率,不使用ASCII全名作为 编译代码中的方法选择器。相反,编译器编写每个 方法名称,然后将该名称与唯一标识符配对 表示运行时的方法运行时系统确保 每个标识符都是唯一的:没有两个选择器是相同的,而且都是相同的 具有相同名称的方法具有相同的选择器。 有人能解释这些吗?此外,如果不同的类具有相同名称的方法,它们是否也具有相同的选择器 不过,它还引用了在编译源代码时替换名称的唯一标识符。。。具有相同名称的所有方法都具有相同的选择器 为此,我参考了一个优秀的选择器: 对于具有相同名称和参数的所有方法,选择器都是相同的—无论定义它们的对象是什么,这些对象是否在类层次结构中相关,或者实际上彼此无关。在运行时,Objective-C转到类并直接询问它,“您是否响应此选择器?”,如果响应,则调用结果函数指针 运行时系统确保每个标识符都是唯一的:没有两个选择器是相同的,并且具有相同名称的所有方法都具有相同的选择器理解Objective-C中选择器的唯一性,objective-c,selector,objective-c-runtime,Objective C,Selector,Objective C Runtime,我在理解苹果指南中描述的“选择器”的部分功能时遇到问题。我已将我感到困惑的部分加粗: 在Objective-C中,选择器有两种含义。可供参考 在源代码消息中使用的方法的名称 指向一个对象但它也引用了 编译源代码时替换名称。compiled 选择器为SEL类型具有相同名称的所有方法都具有 相同的选择器。您可以使用选择器调用 对象这为实现 Cocoa中的目标动作设计模式 方法和选择器为提高效率,不使用ASCII全名作为 编译代码中的方法选择器。相反,编译器编写每个 方法名称,然后将该名称与唯一标识符
以一种糟糕的方式,这是有道理的。如果方法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);