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.
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
. _cmd
is the SEL of the method currently executing. 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; 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). 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).
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).
If you really want to dive deep on how objc_msgSend()
works, I wrote a bit of a guide. Note that it is slightly out of date as it was written before tagged pointers, blocks-as-implementation, and ARC were disclosed. I should update the articles.