我在理解“选择器”功能的一部分时遇到问题,如Apple指南中所述。我把那些让我感到困惑的部分加粗了:
在Objective-C中,选择器有两个含义。它可以用来指代 简单地说,当它在源代码消息中使用时,它的名称 到一个对象。 但它也指的是唯一标识符 在编译源代码时替换名称。编译 选择器是SEL类型。 具有相同名称的所有方法都具有 相同的选择器。您可以使用选择器来调用方法 对象 - 这为实现提供了基础 可可的目标行动设计模式。
方法和选择器为了提高效率,不使用完整的ASCII名称 编译代码中的方法选择器。相反,编译器会编写每个 方法名称放入表中,然后将名称与唯一标识符配对 表示运行时的方法。 运行时系统确保 每个标识符都是唯一的:没有两个选择器是相同的,都是 具有相同名称的方法具有相同的选择器。
任何人都可以解释这些位吗?另外,如果不同的类具有相同名称的方法,它们是否也具有相同的选择器?
答案 0 :(得分:13)
所有选择器都是独一无二的 - 无论是在编译时还是动态地,在运行时通过sel_getUid()
或首选sel_registerName()
(后者在很大程度上是首选,前者仍然是历史原因) - 速度 。
背景故事:要调用方法,运行时需要一个选择器来标识要调用的内容以及将调用它的对象。这就是为什么Objective-C中的每个方法调用都有两个参数:明显且众所周知的self
和不可见的暗示参数_cmd
。 _cmd
是当前正在执行的方法的SEL。也就是说,您可以将此代码粘贴到任何方法中,以查看当前正在执行的方法的名称 - 选择器:
NSLog(@"%@", NSStringFromSelector(_cmd));
请注意,_cmd
不是全局的;它确实是你方法的一个参数。见下文。
通过统一选择器,所有基于选择器的操作都是使用指针相等性测试而不是字符串处理或任何指针取消引用来实现的。
特别是,每次进行方法调用时都会:
[someObject doSomething: toThis withOptions: flags]; // calls SEL doSomething:withOptions:
编译器生成此代码(或非常密切相关的变体):
objc_msgSend(someObject, @selector(doSomething:withOptions:), toThis, flags);
objc_msgSend()
做的第一件事就是检查someObject
是否为零,如果是,则检查是否为短路(nil-eats-message)。下一个(忽略标记的指针)是在someObject
的类中查找选择器(实际上是isa
指针),找到实现,然后调用它(使用尾调用优化)。
发现实现的事情必须快速并且使其非常快,您希望密钥能够尽可能快地找到方法的实现。要执行那个,您希望密钥可以直接使用,并且对流程是全局唯一的。
因此,选择器是唯一的。
它也恰好可以节省内存是一个很好的好处,但如果信息传输速度提高2倍(但不是10倍速,2倍速,甚至2倍内存,2倍速 - ),信使会使用比现在更多的内存。虽然速度至关重要,但内存使用也很重要,当然。)
如果你真的想深入了解objc_msgSend()
的工作方式,我写了bit of a guide。请注意,它稍微过时,因为它是在标记指针,块实现和ARC公开之前编写的。我应该更新文章。
答案 1 :(得分:5)
是。类会共享选择器。
我可以从源代码objc-sel.mm
中提供一个示例,但是当您使用sel_registerUid()
(在@selector()
中的幕后使用)时,
它将输入字符串复制到内部缓冲区(如果字符串之前尚未注册),所有未来的SEL都指向该缓冲区。
这样做可以减少内存使用量,并且更容易进行邮件转发。
答案 2 :(得分:4)
但它也引用了在编译源代码时替换名称的唯一标识符...所有具有相同名称的方法都具有相同的选择器。
为此,我在选择器上引用了一个优秀的blog post:
对于具有相同名称和参数的所有方法,选择器都是相同的 - 无论哪个对象定义它们,这些对象是否在类层次结构中相关,或者实际上彼此无关。在运行时,Objective-C进入类并直接询问它,“你是否响应这个选择器?”,并调用结果函数指针。如果有的话。
运行时系统确保每个标识符都是唯一的:没有两个选择器是相同的,并且所有具有相同名称的方法都具有相同的选择器。
以一种搞砸的方式,这是有道理的。如果方法A和方法B具有完全相同的名称和参数,将它们的选择器存储为一个查找并查询接收器而不是在运行时在两个基本上同等命名的选择器之间进行决定是不是更有效?
答案 3 :(得分:2)
查看SEL类型,您不必定义此选择器所属的类,只需为其指定方法名称,例如:
SEL animationSelector = @selector(addAnimation:forKey:);
例如,您可以将其想象为街道名称。许多城市可以拥有相同的街道名称,但没有城市的街道名称毫无价值。对于选择器来说,你可以定义一个选择器,而无需添加它所在的对象。但是如果没有适合类,它就完全没用了..