Daniel Kennett recently posed the question on Twitter how many of us were defining symbolic constants for use with Key-Value-Coding and Key-Value-Observing. After all, string literals are bad, because they don’t get checked by the compiler, so any typo in them goes unnoticed until you realize your key-value-observer method is not called, or you’re getting back nil where a property really contains a value.
That immediately sent me musing that it would be nice to have something like @selector() for specifying properties as NSStrings. The nice part about @selector() is that, given you’ve turned on the Undeclared Selector warning (-Wundeclared-selector), it will complain if given any selector that the compiler doesn’t know about yet. Which is usually the case if you make a typo.
Five hours later, the idea suddenly came to me: A property’s getter is a selector! And @selector() already does the checking and presents an error message. And one can use NSStringFromSelector() to generate an NSString from any selector. Now, those two are a little wordy, but you can condense that into a neat little macro:
#define PROPERTY(propName) NSStringFromSelector(@selector(propName))
Admitted, this burns a few additional cycles because it turns a string constant into a function call, but NSStringFromSelector() can probably take an existing string constant from inside the SEL data structure, and you can always take it out of your release builds by writing:
#if DEBUG #define PROPERTY(propName) NSStringFromSelector(@selector(propName)) #else #define PROPERTY(propName) @#propName #endif
This turns your property name into the string constant we all know and love in release builds, but does the checking in your debug builds (if you’ve set up your debug builds with a -DDEBUG=1 flag like many people do).
The advantage of this is that you get checked selectors, even when doing key-value-coding and key-value-observing, without any performance impact on your users. If performance is a problem, you can even only use the checked version of PROPERTY() in a special ‘tests’ build of your app. But of course then you can still find yourself flummoxed at a missing KVO notification until you do that special build and it complains.
PS – Thanks to Rob Rix for reminding me of the existence of the # operator, and Jens Ayton for looking up the warning name just when I started wondering why I was getting the warning in one project, but not the other.