2

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

在 Objective-C 中,选择器有两种含义。当它用于对象的源代码消息中时,它可以用来简单地引用方法的名称。但是,它也指的是在编译源代码时替换名称的唯一标识符。编译的选择器是 SEL 类型的。所有具有相同名称的方法都具有相同的选择器。您可以使用选择器来调用对象上的方法——这为在 Cocoa 中实现目标-动作设计模式提供了基础。

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

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

4

4 回答 4

16

所有选择器都是唯一的——无论是在编译时,还是动态的,在运行时通过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,如果是则短路(nil-eats-message)。接下来(忽略标记的指针)是在someObjects 类中查找选择器(isa实际上是指针),找到实现并调用它(使用尾调用优化)。

找到实现的东西必须很快,并且要让它真正快速,你希望找到方法实现的关键尽可能快和稳定。为此您希望密钥可直接使用并且对流程全局唯一。

因此,选择器是唯一的。

它也恰好节省内存是一个非常好的好处,但是如果消息传递速度可以提高 2 倍(但不是 10 倍 2 倍 - 甚至 2 倍内存 2 倍速度 - 而速度是关键,内存使用也很关键,当然)。

如果您真的想深入了解其objc_msgSend()工作原理,我写了一些指南。请注意,由于它是在标记指针、块作为实现和 ARC 被公开之前编写的,所以它有点过时了。我应该更新文章。

于 2012-06-15T14:36:20.783 回答
5

是的。类确实共享选择器。

我可以从源代码中举一个例子objc-sel.mm,但是当你使用sel_registerUid()(在幕后使用@selector())时,它会将输入字符串复制到一个内部缓冲区(如果该字符串之前没有注册过),所有未来的 SEL 都指向该缓冲区.

这样做是为了减少内存使用和更轻松的消息转发。

于 2012-06-15T13:35:27.610 回答
4

但是,它也指的是在编译源代码时替换名称的唯一标识符……所有具有相同名称的方法都具有相同的选择器。

为此,我参考了一篇关于选择器的优秀博文:

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

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

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

于 2012-06-15T13:31:59.030 回答
2

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

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

例如,您可以将其想象为街道名称。许多城市可以有相同的街道名称,但没有城市的街道名称毫无价值。选择器也是如此,您可以定义一个选择器而不添加它所在的对象。但是如果没有合适的类,它完全没有价值。

于 2012-06-15T13:39:54.453 回答