Objective-C Lesson 13: Key-Value Coding
Accessing ivars is usually done through accessor methods or dot-notation. But as a means of indirection, there is another way- Key-Value Coding, or KVC. This is a means of setting and getting the values by using strings- specifically, NSString
, and its corresponding methods, including the very useful stringWithFormat:
.
Methods
KVC involves another layer of abstraction, in the form of two specific methods: valueForKey:
and setValue:forKey:
. These methods work as they do for NSDictionary
(which, unsurprisingly, is built upon the same principles). The key is an NSString
, constructed as a constant or with any one of NSString
’s methods. The code is simple:
NSString *name = [myObject valueForKey:@"name"];
The key is in fact the same name as your variable. It follows a specific sequence of steps:
-
First, it looks for a getter method with the same name. In the above example, it looks for the
name
orisName
(for boolean values) methods; if they are found, it invokes that method and returns that result. -
If the getter methods are not found, the method looks for an instance variable by that name:
name
or_name
.
This is a very important mechanism: valueForKey:
uses the metadata in the runtime stack to look into your custom objects. This ability is not available in other languages. Speaking of other languages, experienced Java programmers are familiar with the concepts of autoboxing- converting int and other scalar types into corresponding object types, such as Integer and Double. KVC is the only Objective-C construct that supports autoboxing- it automatically converts scalar types to NSNumbers
or NSValues
. It will also do the opposite if you set one of these types to a scalar value. Note, however, you must still manually “box” a scalar value before using it in the setValue:ForKey:
method:
[self setValue:[NSNumber numberWithFloat:249.42] forKey:@"price"];
If price is a float, the value will be automatically unboxed.
Key Paths
You can combine KVC and dot syntax to create a key path. Imagine that we have a store class; we can use the following syntax:
[store setValue:[NSNumber numberWithFloat:2.99] forKeyPath:@“gallonOfMilk.price];
That is the same as
store.gallonOfMilk.price = 2.99;
These key paths can be as deep as you want them to be.
Aggregated Results
NSArray *prices = [store valueForKeyPath:@"merchandise.price"];
// Merchandise is an array
If you ask an NSArray
for a value for a key, it will create another array, ask each object for the value specified in the key path, and adds these values to the new array. It basically loops over each value in the array, and sends each object the method valueForKeyPath:@"price"
.
Advanced Operators
-
@count:
count = [store valueForKeyPath:@"merchandise.@count"];
The@count
directive tells the KVC mechanism to get the count of the result of the rest of the key path. -
@sum:
totalPrice = [store valueForKeyPath:@"merchandise.@sum.price"];
The@sum
directive gets the sum of all the requested values- in this case, the sum of all the prices of the merchandise. -
@avg:
avgPrice = [store valueForKeyPath:@"merchandise.@avg.price"];
The@avg
directive gets the average of all the values. -
@min and @max:
minPrice = [store valueForKeyPath:@"merchandise.@min.price"]; maxPrice = [store valueForKeyPath:@"merchandise.@max.price"];
These two are self-explanatory: they return the maximum or minimum of all the values in the range.
-
@distinctUnionOfObjects:
NSArray *brands = [store valueForKeyPath:@"merchandise.@distinctUnionOfObjects.brand"];
This key returns a list of individual distinct values—here, it returns a list of all the brands in our hypothetical store.
Note that, unfortunately, you cannot add your own operators.
One More Application
Imagine that you were creating a lottery application (or more likely, a lottery view within another application). You might have 10 views, each with a number, and you highlight each image as a number is drawn. In this case, you’d probably have ten instance variables, maybe named numberView1
, numberView2
, all the way to numberView10
. To light up the views, you could have a large if…else or switch block, comparing the new value to all the values 1–10:
int nextNum = arc4random() % 10 } 1; // Random number generator, 1–10
switch (nextNum) {
case 1:
// Highlight
break;
// etc.
}
Knowing KVC though, there is a more elegant solution:
[[self valueForKey:[NSString stringWithFormat:@"numberView%d", nextNum]] highlight]; // Assume highlight method exists and works
That method can be found in this sample project: Find it here.
This post is part of the Learn Objective-C in 24 Days course.
Previous Lesson | Next Lesson |