简体   繁体   English

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

[英]Understanding uniqueness of selectors in Objective-C

I am having problem understanding part of the function of "selectors", as described in Apple's guide. 我正在理解“选择器”功能的一部分,如Apple指南中所述。 I've bolded the parts where I am getting confused: 我把那些让我感到困惑的部分加粗了:

In Objective-C, selector has two meanings. 在Objective-C中,selector有两个含义。 It can be used to refer simply to the name of a method when it's used in a source-code message to an object. 它可以用于简单地引用方法的名称,当它在对象的源代码消息中使用时。 It also, though, refers to the unique identifier that replaces the name when the source code is compiled. 但是,它也指的是在编译源代码时替换名称的唯一标识符。 Compiled selectors are of type SEL. 编译选择器的类型为SEL。 All methods with the same name have the same selector. 具有相同名称的所有方法都具有相同的选择器。 You can use a selector to invoke a method on an object—this provides the basis for the implementation of the target-action design pattern in Cocoa. 您可以使用选择器来调用对象上的方法 - 这为在Cocoa中实现目标操作设计模式提供了基础。

Methods and Selectors For efficiency, full ASCII names are not used as method selectors in compiled code. 方法和选择器为了提高效率,完整的ASCII名称不用作编译代码中的方法选择器。 Instead, the compiler writes each method name into a table, then pairs the name with a unique identifier that represents the method at runtime. 相反,编译器将每个方法名称写入表中,然后将名称与表示运行时方法的唯一标识符配对。 The runtime system makes sure each identifier is unique: No two selectors are the same, and all methods with the same name have the same selector. 运行时系统确保每个标识符都是唯一的:没有两个选择器是相同的,并且所有具有相同名称的方法都具有相同的选择器。

Can anyone explain these bits? 任何人都能解释这些位吗? Additionally, if different classes have methods with the same name, will they also have the same selector? 另外,如果不同的类具有相同名称的方法,它们是否也具有相同的选择器?

All selectors are uniqued -- both at compile time and, dynamically, at runtime through sel_getUid() or the preferred sel_registerName() (the latter being largely preferred, the former still around for historical reasons) -- for speed . 所有选择器都是唯一的 - 无论是在编译时还是动态地,在运行时通过sel_getUid()或首选的sel_registerName() (后者在很大程度上是首选,前者仍然是历史原因) - 速度

Back story: to call a method, the runtime needs a selector that identifies what is to be called and an object that it will be called on. 背景故事:为了调用方法,运行时需要一个选择器来标识要调用的内容以及一个将被调用的对象。 This is why every single method call in Objective-C has two parameters: the obvious and well known self and the invisible, implied, parameter _cmd . 这就是为什么Objective-C中的每个方法调用都有两个参数:明显的和众所周知的self和不可见的暗示参数_cmd _cmd is the SEL of the method currently executing. _cmd是当前正在执行的方法的SEL。 That is, you can paste this code into any method to see the name -- the selector -- of the currently executing method: 也就是说,您可以将此代码粘贴到任何方法中,以查看当前正在执行的方法的名称 - 选择器:

NSLog(@"%@", NSStringFromSelector(_cmd));

Note that _cmd is not a global; 请注意_cmd 不是全局的; it really is an argument to your method. 它确实是你方法的一个参数。 See below. 见下文。

By uniquing the selectors, all selector based operations are implemented using pointer equality tests instead of string processing or any pointer de-referencing at all. 通过单选选择器,所有基于选择器的操作都是使用指针相等性测试而不是字符串处理或任何指针解引用来实现的。

In particular, every single time you make a method call: 特别是,每次进行方法调用时:

[someObject doSomething: toThis withOptions: flags]; // calls SEL doSomething:withOptions:

The compiler generates this code (or a very closely related variant): 编译器生成此代码(或非常密切相关的变体):

objc_msgSend(someObject, @selector(doSomething:withOptions:), toThis, flags);

The very first thing objc_msgSend() does is check to see if someObject is nil and short-circuit if it is (nil-eats-message). objc_msgSend()做的第一件事就是检查someObject是否为nil,如果是(nil-eats-message)则是短路。 The next (ignoring tagged pointers) is to look up the selector in someObject s class (the isa pointer, in fact), find the implementation , and call it (using a tail call optimization). 下一个(忽略标记的指针)是在someObject的类中查找选择器(实际上isa指针), 找到实现并调用它(使用尾调用优化)。

That find the implementation thing has to be fast and to make it really fast, you want the key to finding the implementation of the method to be as fast and stable as possible. 发现实现的事情必须快速并且使其非常快,您希望关键是找到尽可能快速和稳定的方法的实现。 To do that , you want the key to be directly usable and globally unique to the process. 要做到这一点 ,您希望密钥可以直接使用,并且对流程是全局唯一的。

Thus, the selectors are uniqued. 因此,选择器是唯一的。

That it also happens to save memory is an fantastic benefit, but the messenger would use more memory than it does today if messenging could be made 2x faster (but not 10x for 2x -- or even 2x memory for 2x speed -- while speed is critical, memory use is also critical, certainly). 这也恰好可以节省内存,但是信使会使用比现在更多的内存,如果信息传输速度提高2倍(但不是10倍速2倍 - 甚至是2倍内存,2倍速 - 而速度是关键,内存使用也很关键,当然)。

If you really want to dive deep on how objc_msgSend() works, I wrote a bit of a guide . 如果你真的想深入了解objc_msgSend()如何工作,我写了一些指南 Note that it is slightly out of date as it was written before tagged pointers, blocks-as-implementation, and ARC were disclosed. 请注意,它稍微过时,因为它是在标记指针,块实现和ARC公开之前编写的。 I should update the articles. 我应该更新文章。

Yes. 是。 Classes do share selectors. 类会共享选择器。

I can give an example from the source code objc-sel.mm , but when you use sel_registerUid() (used behind the scenes in @selector() ), It copies the input string into an internal buffer (if the string hasn't been registered before), for which all future SELs point to. 我可以从源代码objc-sel.mm给出一个示例,但是当你使用sel_registerUid() (在@selector()后面的场景中使用)时,它sel_registerUid()输入字符串复制到内部缓冲区中(如果字符串没有之前已经注册过),所有未来的SEL指向。

This is done for less memory usage, and easier message forwarding. 这样做是为了减少内存使用,并简化消息转发。

It also, though, refers to the unique identifier that replaces the name when the source code is compiled... All methods with the same name have the same selector. 但是,它也引用了在编译源代码时替换名称的唯一标识符...所有具有相同名称的方法都具有相同的选择器。

For this, I refer to an excellent blog post on selectors: 为此,我参考了一篇关于选择器的优秀博客文章

A selector is the same for all methods that have the same name and parameters — regardless of which objects define them, whether those objects are related in the class hierarchy, or actually have nothing to do with each other. 对于具有相同名称和参数的所有方法,选择器都是相同的 - 无论哪个对象定义它们,这些对象是否在类层次结构中相关,或者实际上彼此无关。 At runtime, Objective-C goes to the class and outright asks it, "Do you respond to this selector?", and calls the resulting function pointer if it does. 在运行时,Objective-C进入类并直接询问它,“你是否响应这个选择器?”,并调用结果函数指针(如果有)。

The runtime system makes sure each identifier is unique: No two selectors are the same, and all methods with the same name have the same selector. 运行时系统确保每个标识符都是唯一的:没有两个选择器是相同的,并且所有具有相同名称的方法都具有相同的选择器。

In a screwed up way, this makes sense. 以一种搞砸的方式,这是有道理的。 If Method A and Method B have the exact same name and arguments, wouldn't it be more efficient to store their selector as one lookup and query the receiver instead of deciding between two basically equally named selectors at runtime? 如果方法A和方法B具有完全相同的名称和参数,将它们的选择器存储为一个查找并查询接收器而不是在运行时在两个基本上同等命名的选择器之间进行决定是不是更有效?

Look at the SEL type, you don't have to define which class this selector is from, you just give it a method name, for example: 查看SEL类型,您不必定义此选择器所属的类,只需为其指定方法名称,例如:

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

You can imagine it as a streetname, for example. 例如,您可以将其想象为街道名称。 Many cities can have the same streetnames, but a streetname without a city is worthless. 许多城市可以拥有相同的街道名称,但没有城市的街道名称毫无价值。 Same for selectors, you can define a selector without adding the object where it's in. But it's complete worthless without fitting class.. 对于选择器来说,你可以定义一个选择器,而无需添加它所在的对象。但是如果没有适合类,它就完全没用了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM