Add Meaning To Key Value Strings

May 22nd, 2007

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:

  1. Apple can change the implementation at any time to something that suits the system better.
  2. 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?

13 Responses to “Add Meaning To Key Value Strings”

  1. Mike Abdullah Says:

    Now I may be missing the point entirely, but to address the original point of strings for KVC:

    This is something that does kind of irk me as well. Mainly the fact that you can easily mistype a string and the compiler won’t give a damn. How about just making better use of string constants instead? Avoid typing @”key” in code and instead have something like:

    extern NSString * const MyClassKeyForSomething;

    [object valueForKey:MyClassKeyForSomething];

  2. Christopher Humphries Says:

    Thanks, now I learned something new this morning.

  3. Sanjay Samani (ssanchez) Says:

    Re: Mike Abdullah’s comment about using string constants.

    I was taught in my early days to not use string literals and instead use string constants, to avoid typos.

    When I started using KVC heavily, I thought this through quite hard and decided to stick with string literals because:
    – Refactoring is not particularly difficult (search and replace)
    – Doesn’t help with refactoring NIB bindings
    – When using Core Data, doesn’t help with refactoring Entity.strings files
    – Debugging KVC naming issues isn’t that hard. OK its at run time rather than compile, but some unit testing can help out.
    – Xcode does auto completion (I’m not sure whether you have to write the string literal once for this to work)
    – I found that with syntax highlighting, the string literal was more readable.

    For a while I didn’t write any accessors for my Core Data Entities, then realised that you are heavily recommended to do so for performance and valueForKey: will actually look for an appropriate get accessor. So I only ended up using string literal key names when programmatically binding or calling setKeys:triggerChangeNotificationsForDependentKey:

    ssanchez

  4. Pierre Says:

    Hello,
    I agree that string literals in code are a bad idea. The problem for me is refactoring : it is very laborious to find/replace relevant instances of a string used as key.
    However, as far as I understood, this is a point that could be addressed in Objective-C 2.0, with the notion of “property”. Or at least, I hope so.

    Regards,
    Pierre

  5. Kenneth Ballenegger Says:

    Hmmm, well the Selector thing wasn’t made up by Apple, wheras KVC was. The selector thing came from Objective-C itself, and its basic framework of classes, when it still had Object as root class instead of the NSObject Apple gave us. When Apple made Cocoa (well, actually when NeXTStep made Cocoa) they rewrote the root class to suite their needs and that’s where they added the KVC, but kept the SEL system that was already there.

  6. Paul F Says:

    Hi-

    I have a somewhat different opinion of this issue. I like it very much.

    I love to be able to make use of this sort of thing in my code. It may stem from my experience as a Python developer where objects are explicitly dictionaries and can be accessed either by string (foo[“x”]) or as a property (foo.x). Being able to select object fields in this way at runtime brings huge expressive power and flexibilty. Look at the way that parameters are specified to Core Image filters for example. It would be much harder to interface with these without KVC.

    Admitiedly, there is a cost to this. If you don’t want to use it with your own objects why not just use foo = [myObject foo] and [myObject setFoo: foo] instead. It’s cleaner syntax than something like @keycode() or kMySyntaxForKeySomething, at least in my opinion. The KVC methods call these setters anyway, do they not?

    Cheers,
    Paul

  7. Taybin Says:

    I think the problem people have with using strings isn’t the string part, but the runtime binding part. And runtime binding is a big part of dynamic languages.

    As for the lack of compile time checking, well, IMHO, that’s why unit testing is so popular nowadays, as a replacement for static variables.

  8. Scott Stevenson Says:

    In the general sense, I see what you’re getting at. In practice, I’m not sure you’d really gain much in the way of productivity by creating a new type. Some things to consider:

    1. Conceptually, the way you use KVC is very similar to the way you use a dictionary. I don’t know for sure if that’s a coincidence but I doubt it.

    2. There are a number of sitatuation where you’d want to iterate through a list of keys that aren’t literally typed in code. In those situations, the keycode bit falls apart somewhat.

    In my opinion, one of the major benefits of Objective-C is the simplicity of the syntax. I don’t know if the advantages justify the additional learning cost for those who are coming to the language for the first time. It strikes me as a bit too cryptic.

  9. mmalc Says:

    Kenneth Ballenegger wrote:
    * the Selector thing wasn”™t made up by Apple, wheras KVC was.

    This is simply not true. I don’t remember exactly when KVC first appeared, but I think it was around 1994, perhaps with EOF.

    * The selector thing came from Objective-C itself, and its basic framework of classes, when it still had Object as root class instead of the NSObject Apple gave us.

    NSObject appeared in Foundation in about 1993…

    * When Apple made Cocoa (well, actually when NeXTStep made Cocoa)

    … the company “NeXT” developed OPENSTEP…

    mmalc

  10. mmalc Says:

    Pierre wrote:
    * […] it is very laborious to find/replace relevant instances of a string used as key.
    * However, as far as I understood, this is a point that could be addressed in Objective-C 2.0, with the notion of “property”.

    Properties and KVC are orthogonal technologies.

    Going by what’s written at http://developer.apple.com/leopard/overview/tools.html, properties provide a means to declare explicitly the semantics of a “member” (I’m avoiding simply rewriting “property” — strictly it looks like a declared property need not be backed by an ivar), and (if you wish) to synthesise the corresponding accessor methods.

    You could therefore use properties independently of KVC, or you could use KVC to access properties.

    mmalc

  11. Chuck Says:

    I don’t really think KVC is analogous to selectors. There is a fixed, finite set of selectors, and they are simple. Your program keeps a table of them at all times. The idea of KVC strings, though, is that they are named attributes that might not exist until right before they are used. Selectors have no analogue to @”fanController.arrangedObjects.favoriteBand.name”. I’d say they’re more similar to NSDictionary’s use to pass arbitrary options and attributes to methods.

    You could create some NSKey type that is initialized with a string and functionally identical to a string, but I really don’t see how that’s so useful. I mean, in all honesty, have you ever lost track of the meaning of a KVC string in a way that a different but completely equivalent type would help?

    Maybe I’m shortsighted or just not understanding, but it seems to me that this would just lead to slightly more code and make the type system slightly more complicated, neither of which I consider to be a very good thing.

  12. mmalc Says:

    The raison d’être of KVC is precisely that it defers decisions until runtime. The canonical example is perhaps the table view data source — see http://developer.apple.com/documentation/Cocoa/Conceptual/TableView/Tasks/UsingTableDataSource.html and in particular:

    theValue = [theRecord objectForKey:[aTableColumn identifier]];

    Rather than using a dictionary as the model object, you could use KVC to access the properties of any instance:

    theValue = [theRecord valueForKey:[aTableColumn identifier]];

    There’s no way for the compiler to determine that the identifier will be a valid key.

    Moreover:

    Chuck wrote:
    * Selectors have no analogue to @”fanController.arrangedObjects.favoriteBand.name”.

    Indeed. Key-value coding includes key *paths*, not just keys. This offers considerable power, flexibility, and economy.

    * I don”™t really think KVC is analogous to selectors. There is a fixed, finite set of selectors,

    This isn’t really true. You can create as many selectors as you want at runtime; there is no need for there to be a corresponding implementation (as so many people discover when they misspell a selector name…).

    mmalc

  13. Jesper Says:

    A bit late to the party, but what’s wrong with this?

    #define KVC(X) [NSString stringWithUTF8String: #X ]

    KVC(foo.bar.quux) yields that as an NSString. Replace the stringWithUTF8String: selector with stringWithCString:encoding: and the appropriate encoding at your leisure. It’s a shame you can’t bolt @ and the stringified result together with ## (it bums out), and a shame you can’t define it as @kvc(). Oh well.

Comments are Closed.

Follow the Conversation

Stay up-to-date by subscribing to the Comments RSS Feed for this entry.