Fraser Speirs recently tweeted some comments about strings in code:
“I think that I particularly hate strings in code that have programmatic meaning. I’m looking at you, KVC. And you, KVO.”
Reading this was one of those “yeah, that sucks!” moments, and it got me thinking about how the situation might be improved. The problem isn’t that strings are used to drive the logical flow of an application – this is nothing new. Since long before KVC (Key Value Coding) and KVO (Key Value Observing) came along, Cocoa has employed data-driven techniques for dictating logical flow in an application.
Consider Objective-C selectors. These are essentially “key value coded methods.” A selector is conceptually a string, so much so that Objective C offers a shorthand for creating selectors directly from text in your code:
SEL mySelector = @selector(doSomething:);
Apple even recognizes the desire to create selectors dynamically at runtime, and offers the NSSelectorFromString.
A great thing about selectors is that they’re only conceptually strings. The “SEL” data type is defined as an opaque data structure pointer. If you examine the memory of the structure (as of 10.4) you’ll see that it looks quite a lot like a string, which is handy for debugging, but it’s not guaranteed to be a string.
The opaque “obstruction” Apple has inserted between the conceptual and implementation levels mean two things:
- Apple can change the implementation at any time to something that suits the system better.
- Compilers can do first-class type checking on methods and functions that expect selector arguments.
Compare the selector situation with its much younger sibling, who seems to have been born in a hurry and without as much planning:
id myValue = [myObject valueForKey:@"hello"];
In contrast to selectors, where semantic meaning is locked into the explicit SEL data type, here we must derive meaning from the context of the constant string data. We only know that “hello” is a key (or we hope it is) because it is preceded by a “valueForKey:” method signature. It would have been a bit more cumbersome for Apple and developers if they’d defined a “KeyCode” type and required it in all key value coding and observing method parameters:
id myValue = [myObject valueForKey:@keycode(hello)];
But suddenly the string itself has a lot more meaning. You might argue that in this example, the problem is the same, but there’s just more context to surround the literal string. That’s kind of true, but with a true compiler type the meaning sticks with the variable long after it’s typed or viewed in the code. Methods could dynamically distinguish plain strings from coding strings, a compiler could produce warnings based on semantic knowledge of keys (“that string doesn’t conform to key coding syntax”), and runtime statistics could be gathered about the number of keys that are used by your application (whatever that is worth).
I haven’t really thought this through too far, but I’m curious why Apple chose not to mimic their own treatment of selectors when it came time to base another critical technology on conceptual strings. I wonder if a “strongly typed” set of key value coding methods as a wrapper on Foundation’s methods would prove useful in my projects, or be a misguided waste of time. What do you think?