[英]Understanding uniqueness of selectors in Objective-C
我正在理解“选择器”功能的一部分,如Apple指南中所述。 我把那些让我感到困惑的部分加粗了:
在Objective-C中,selector有两个含义。 它可以用于简单地引用方法的名称,当它在对象的源代码消息中使用时。 但是,它也指的是在编译源代码时替换名称的唯一标识符。 编译选择器的类型为SEL。 具有相同名称的所有方法都具有相同的选择器。 您可以使用选择器来调用对象上的方法 - 这为在Cocoa中实现目标操作设计模式提供了基础。
方法和选择器为了提高效率,完整的ASCII名称不用作编译代码中的方法选择器。 相反,编译器将每个方法名称写入表中,然后将名称与表示运行时方法的唯一标识符配对。 运行时系统确保每个标识符都是唯一的:没有两个选择器是相同的,并且所有具有相同名称的方法都具有相同的选择器。
任何人都能解释这些位吗? 另外,如果不同的类具有相同名称的方法,它们是否也具有相同的选择器?
所有选择器都是唯一的 - 无论是在编译时还是动态地,在运行时通过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)则是短路。 下一个(忽略标记的指针)是在someObject
的类中查找选择器(实际上isa
指针), 找到实现并调用它(使用尾调用优化)。
发现实现的事情必须快速并且使其非常快,您希望关键是找到尽可能快速和稳定的方法的实现。 要做到这一点 ,您希望密钥可以直接使用,并且对流程是全局唯一的。
因此,选择器是唯一的。
这也恰好可以节省内存,但是信使会使用比现在更多的内存,如果信息传输速度提高2倍(但不是10倍速2倍 - 甚至是2倍内存,2倍速 - 而速度是关键,内存使用也很关键,当然)。
如果你真的想深入了解objc_msgSend()
如何工作,我写了一些指南 。 请注意,它稍微过时,因为它是在标记指针,块实现和ARC公开之前编写的。 我应该更新文章。
是。 类会共享选择器。
我可以从源代码objc-sel.mm
给出一个示例,但是当你使用sel_registerUid()
(在@selector()
后面的场景中使用)时,它sel_registerUid()
输入字符串复制到内部缓冲区中(如果字符串没有之前已经注册过),所有未来的SEL指向。
这样做是为了减少内存使用,并简化消息转发。
但是,它也引用了在编译源代码时替换名称的唯一标识符...所有具有相同名称的方法都具有相同的选择器。
对于具有相同名称和参数的所有方法,选择器都是相同的 - 无论哪个对象定义它们,这些对象是否在类层次结构中相关,或者实际上彼此无关。 在运行时,Objective-C进入类并直接询问它,“你是否响应这个选择器?”,并调用结果函数指针(如果有)。
运行时系统确保每个标识符都是唯一的:没有两个选择器是相同的,并且所有具有相同名称的方法都具有相同的选择器。
以一种搞砸的方式,这是有道理的。 如果方法A和方法B具有完全相同的名称和参数,将它们的选择器存储为一个查找并查询接收器而不是在运行时在两个基本上同等命名的选择器之间进行决定是不是更有效?
查看SEL类型,您不必定义此选择器所属的类,只需为其指定方法名称,例如:
SEL animationSelector = @selector(addAnimation:forKey:);
例如,您可以将其想象为街道名称。 许多城市可以拥有相同的街道名称,但没有城市的街道名称毫无价值。 对于选择器来说,你可以定义一个选择器,而无需添加它所在的对象。但是如果没有适合类,它就完全没用了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.