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

S.J*_*S.J 2 objective-c objective-c-runtime selector

我正在理解"选择器"功能的一部分,如Apple指南中所述.我把那些让我感到困惑的部分加粗了:

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

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

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

bbu*_*bum 13

所有选择器uniqued -既在编译时和地,动态地在运行时通过sel_getUid()或优选的sel_registerName()(后者主要优选的,前者仍然围绕历史原因) - 速度.

背景故事:为了调用方法,运行时需要一个选择器来标识要调用的内容以及一个将被调用的对象.这就是为什么Objective-C中的每个方法调用都有两个参数:明显的和众所周知的self以及不可见的暗示参数_cmd. _cmd是当前正在执行的方法的SEL.也就是说,您可以将此代码粘贴到任何方法中,以查看当前正在执行的方法的名称 - 选择器:

NSLog(@"%@", NSStringFromSelector(_cmd));
Run Code Online (Sandbox Code Playgroud)

请注意,_cmd不是一个全球性的; 它确实是你方法的一个参数.见下文.

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

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

[someObject doSomething: toThis withOptions: flags]; // calls SEL doSomething:withOptions:
Run Code Online (Sandbox Code Playgroud)

编译器生成此代码(或非常密切相关的变体):

objc_msgSend(someObject, @selector(doSomething:withOptions:), toThis, flags);
Run Code Online (Sandbox Code Playgroud)

首先要做的objc_msgSend()是检查是否someObject为零和短路(如果是)(nil-eats-message).下一个(忽略标记的指针)是在someObjects类中查找选择器(isa实际上是指针),找到实现并调用它(使用尾调用优化).

找到实现的事要快,并使其非常快,你想要的关键是找到方法的实现要以最快的速度和稳定越好.要做到这一点,您希望密钥可以直接使用,并且对流程是全局唯一的.

因此,选择器是唯一的.

这也恰好可以节省内存,但是信使会使用比现在更多的内存,如果信息传输速度提高2倍(但不是10倍速2倍 - 甚至2倍内存,2倍速 - 而速度是关键,内存使用也很关键,当然).

如果你真的想深入了解如何objc_msgSend()运作,我写了一些指南.请注意,它稍微过时,因为它是在标记指针,块实现和ARC公开之前编写的.我应该更新文章.

  • 更新后的objc_msgSend()游览会非常有趣. (2认同)

Ric*_*III 5

是.类会共享选择器.

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

这样做是为了减少内存使用,并简化消息转发.

  • 不是清晰度问题,而是准确性.这是速度,*速度*,**速度**.查看`objc_msgSend()`的缓存查找实现; 每个方法调用 - 当你启动一个设备时,它们都有数百万个 - 依赖于选择器的唯一性,允许缓存查找和消息调度尽可能快(或者像团队知道如何制作一样快它,无论如何:). (2认同)