a glob of nerdishness

July 22, 2008

Comparing Objective-C selectors

written by natevw @ 9:58 pm

The key feature Objective-C adds to C is the concept of sending messages to objects. In most lines of code, the messages and receiving objects are basically intertwined: [thisObject getsThisMessage]. But just as you can pass around an object without necessarily sending it any messages, you can also deal with messages without sending them to objects. This is done via message selectors, which can be stored in variables of the single SEL type.

Apple’s Objective-C reference doesn’t make very many guarantees about what is stored in a SEL. It is a mostly opaque structure, to be probed only by functions “in the know”. But one perfectly normal thing to do is to ask an object if it will respond to a particular message selector, or if the action associated with a particular selector is currently enabled. To answer questions like this, the object may want to check if the selector in question is equal to a known selector.

Therein lies the rub. Until 10.5, the Objective-C runtime didn’t provide any function to compare these opaque selectors. The documentation does say that “The runtime system makes sure each identifier is unique: No two selectors are the same”. While this is not completely clear, it is understood by even Apple’s sample code (eg) to mean that the contents of identical SEL structs were identical, and thus could be compared using C’s == equality operator.

Apple is extremely unlikely to change this behaviour lightly (Update: it won’t change), as it would basically break binary compatibility across much code regardless of what the documentation is actually promising. However, the fact that the Objective-C 2.0 runtime added a sel_isEqual() function seems to imply that they do kinda sorta maybe kinda reserve the right to start treating SELs as a truly opaque type. (Even then, I can’t think of a good reason why the bitwise-equivalence would change.) But all this aside…there is now a function we can use.

Unfortunately, the sel_isEqual() function is defined in <objc/runtime.h> — a header that Foundation does not include — and doesn’t exactly blend in with the kind of high-level code discussed here. I’ve filed a bug (rdar://6094233) requesting the addition of an NSEqualSelectors() function next to NSStringFromSelector() and NSSelectorFromString(), but in the meantime I’ve written some wrapper code that is both “legacy runtime” and Objective-C 2.0 compatible:


There’s not really anything to it, it basically just wraps an inline function around sel_isEqual() if available, or does the old == comparison if not. I thought about moving the runtime function wrapper implementation into its own code so as not to leave all the runtime functions in the namespace; this might be something to consider for your own use, but this version optimizes for low overhead. Hopefully within a few years, link-time optimization will be ours, but even then keeping everything in one header is probably the best way to distribute this tiny little wrapping hack.