Learn Objective-C: Advanced KVC

Last week’s Lesson covered the basics of KVC, and it is those features that would be used 90% of the time. But KVC has some more to offer. Let’s take a look.

Batch Processing

Another one of KVC’s methods is dictionaryWithValuesForKeys:. The single argument is an array of strings. From that, the invokes valueForKey: with each key, and returns a dictionary with the keys and values. For example, to get some attributes of a piece of merchandise from our hypothetical store, we could use the following code:

  1. Merchandise *product = [[store valueForKeyPath:@"merchandise"] lastObject];   // Last object in array
  2. NSArray *keys = [NSArray arrayWithObjects: @"brand", @"price", @"department", @"shelf", @"netWeight", nil];
  3. NSDictionary *productInfo = [product dictionaryWithValuesForKeys:keys];

You could then use this dictionary in an inventory tracking program, for example.

This method also has a counterpart, setValuesForKeysWithDictionary:. Called on an instance of any object, this method goes through each key in the dictionary, and replaces that key’s value in the object with the new value.

nil Values

Here is one minor issue that you might run into with KVC. You can’t put nil into a dictionary (or array for that matter), because nil tells the collection that it’s the end of data. Therefore, you have to use [NSNull null] instead, and that’s the value you’ll get back if you call valueForKey: on an object type. But what happens if you try to set nil for a scalar type, such as an integer? By default, you get an error at runtime. Therefore, you have to override setNilValueForKey: and define your own implementation. In our store, the method might look like this:

  1. - (void)setNilValueForKey:(NSString *)key {
  2.     if ([key isEqualToString:@"price"])
  3.         [self setValue:[NSNumber numberWithInt:0] forKey:@"price"];
  4.     else
  5.         [super setNilValueForKey:key];
  6. }

Error Handling

What happens if you try to access or set a value for a key that doesn’t exist? In general, you get the compiler or runtime warning that looks like “this class is not key value coding-compliant for the key someKeyThatDoesn’tExist.” Fortunately, when KVC encounters this it will ask the class what to do. As such, there are two methods that you can override: valueForUndefinedKey: and setValue:forUndefinedKey:.

A simple way to handle this issue is to simply tell the user that the key is invalid. So to protect our store from crashes, we’re going to just do that:

  1. - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
  2.     NSLog(@"Cannot set anything for this key. The key %@ is not valid.", key);
  3. }
  4.  
  5. - (void)valueForUndefinedKey:(NSString *)key {
  6.     NSLog(@"Cannot get value for this key. The key %@ is not valid.", key);
  7. }

Obviously, KVC is a very powerful feature, and allows you to build very robust, abstract applications. In fact, it is the core data access mechanism in Core Data, Apple’s framework for accessing data from a database. But that’s a complex topic for another day.

This post is part of the Learn Objective-C in 24 Days course.

Author: Feifan Zhou