
15 Most Recent [RSS]
More...
|
Safe key-value-coding
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.
I've added this code to my UKHelperMacros collection of macros, which is available on my source code page.
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.
Mike Abdullah writes: I suppose there is one downside: If you've got a scenario like this:
- (BOOL)isEditable;
- (void)setEditable:(BOOL)flag;
The key is "editable" but there's no @selector(editable) already in existence.
|
Uli Kusterer replies: ★ Have you tried just checking the "undeclared selectors" checkbox? That will only apply the setting to .m and .mm files, and not to .c files. If you add it to the OTHER_CFLAGS, of course it'll end up in C as well.
I do wonder whether there's an OTHER_OBJCFLAGS, though...
|
Uli Kusterer replies: ★ Mike, yes, properties with customized getter names are one downside. Also, this won't work for key paths, only for single keys. One could probably create macros that generate a key path, but that would use commas and would have to be a macro like KEYPATH3(foo,bar,baz) instead of just @property(foo.bar.baz).
|
Ben Cohen writes: This is useful. Thanks.
I wish there was a way to use constants in Interface Builder. I've lost count of the number of times I've had KVO related errors due to miss typing key paths in IB.
|
Uli Kusterer replies: ★ Ben, have you filed a bug about this with Apple? I have. Add your voice to the list of people asking for it, it'll increase the likelihood of actually getting it. Besides, it's probably a nice project for an Apple intern. |
|  |