KVC (Key Value Coding) is a pretty great technology. It’s used throughout the Cocoa APIs, and you might not even realise you’re using it. One of the more common tasks which makes use of KVC is sorting with
NSSortDescriptor objects. Here’s a quick example snippet:
One of the great aspects to using a sort descriptor with KVC like this is that you don’t have to worry about the type that
-age will return. It could be an
NSString or an
NSNumber, and KVC and
NSSortDescriptor will take care of it for you.
The Problem: String Literals
There’s one really big downside to KVC though, which is that you need to use string literals for the ‘Key’ part of ‘Key Value Coding’. This makes your code fragile in a number of ways.
- You need to specify them in multiple places
- Renaming the property or method means you have to update multiple strings
- Typos won’t be discovered until run-time, when you’ll get an exception
One approach to try and reduce these problems is to create a constant string for every key you want to use with KVC. This solves the first issue, reduces the second one (you still have to remember to update the constant key string when re-factoring), but doesn’t solve the third.
A little while back, Uli Kusterer published a blog post describing a macro that, when used with the
-Wundeclared-selector compiler flag, would throw a compiler warning for keys that don’t exist as a method.
This solves all of the problems with string literals above, but has one other problem; you will only get a warning if the key does not match up with a selector on any class. This means that the following will compile without warning, but throw an exception at run-time:
The Key() Macro
In order to be confident that you’re not going to get an exception at run-time, you need to check that instances of a particular class respond to the selector, not just check that the selector has been declared. Recently I decided to dedicate a few hours to trying to come up with a macro that you can use like this:
The first breakthrough in trying to create such a macro came when I realised that you could get a compile-time check of whether or not a class responds to a selector, while not actually causing anything to happen at run-time, by using a typecast
You can wrap this up in a macro like this:
But the macro needs to not only check that the class responds to the key, it needs to return the key as an
NSString. As far as I knew at the time, it was impossible to write a C macro that executed one expression, and returned the value of a second expression. After a few hours of research and experimentation, I discovered a neat trick: you can use C’s ternary operator to do exactly this, like so:
This macro will run
expr1(arg), and return the value of
expr2(arg). This trick can be combined with the typecast nil trick, to give use a fully functional
The Actual Macro
I’ve also shown a variant that allows you to get a KVC key for a method declared on a protocol, not on a class itself.
In theory, using these macros is slightly slower than just using a string literal (or constant string), but the overhead is so tiny (messaging nil, then evaluating a single ternary branch) that it should be negligible. With compiler optimisation turned on, the ternary branch should (I hope) be optimised out, and the messaging nil part may also be optimised out (although again, the performance hit should be negligible if it isn’t). Despite that, I have used an ifdef to shortcut the macro in Release builds:
Now you can safely use KVC with compile-time checking.
If you aren’t using ARC, you will need to enable the -Wundeclared-selector compiler warning (it shows up in ‘Build Settings’ as ‘Undeclared Selector’ in recent versions of Xcode 4). Under ARC, you don’t need to turn this warning on for these macros to work - undeclared selectors will cause build errors no matter what (which is probably better for this particular situation). Hopefully you find these macros as useful as I do!