derives
Cocoa derives the name of an accessor from a string name at runtime through a mechanism called key–value coding, or simplyKVC. The key is a string (an NSString) that names the value to be accessed.The basis for key–value coding is the NSKeyValueCoding protocol, an informal protocol (it is actually a category) to which NSObject (and therefore every object) conforms. Key–value coding is a big subject; see APPle’sKey-Value Coding Programming Guide for full information.
The fundamental key–value coding methods are valueForKey: andsetValue:forKey:. When one of these methods is called on an object, the object is introspected. In simplified terms, first the appropriate accessor is sought; if it doesn’t exist, the instance variable is accessed directly.
A class is key–value coding compliant (or KVC compliant) on a given key if it implements the methods, or possesses the instance variable, required for access via that key. An attempt to access a key for which a class isnot key–value coding compliant will cause an exception at runtime: “This class is not key value coding-compliant for the key keyName.” (The last word in that ERROR message, despite the lack of quotes, is the key string that caused the trouble.)
So, for example, suppose the call is this:
[myObject setValue:@"Hello" forKey:@"greeting"];
First, a method setGreeting: is sought in myObject; if it exists, it is called, passing @"Hello" as its argument. If that fails, but if myObject has an instance variable called greeting (or _greeting), the value @"Hello" is assigned directly to that ivar. If that fails, an exception is thrown and the app will crash.
WARNING
The key–value coding mechanism can bypass completely the privacy of an instance variable! Cocoa knows that you might not want to allow that, so a class method accessInstanceVariablesDirectly is supplied, which you can override to return NO (the default is YES).
Both valueForKey: and setValue:forKey: require an object as the value. Your accessor’s signature (or, if there is no accessor, the instance variable itself) might not use an object as the value, so the key–value coding mechanism converts for you. Numeric types (including BOOL) are expressed as an NSNumber; other types (such as CGRect and CGPoint) are expressed as an NSValue.
Another useful pair of methods is dictionaryWithValuesForKeys: and setValuesForKeysWithDictionary:, which allow you to get and set multiple key–value pairs by way of an NSDictionary with a single command.
Key–value coding allows you, in effect, to decide at runtime, based on an NSString, what accessor to call. By using an NSString instead of an actual accessor method call, you’re throwing away compile-time checking as to the message you’re sending. moreover, KVC is agnostic about the actual class of the object you’re talking to; you can send valueForKey: toany object and successfully get a result, provided the class of that object is KVC compliant for that key, so you’re throwing away compile-time checking as to the object you’re sending the message to. These are both strong advantages of key–value coding, and I often find myself using it because of them.
Here’s an example of key–value coding used in my own code. In a flashcard app, I have a class Term, representing a Latin term, that defines many instance variables. Each card displays one term, with its instance variables shown in different text fields. If the user taps any of three text fields, I want the interface to change from the term that’s currently showing to the next term whose value is different for the instance variable that this text field represents. Thus this code is the same for all three text fields; the only difference is what instance variable to consider as we hunt for the next term to be displayed. By far the simplest way to express this parallelism is through key–value coding:
NSinteger tag = g.view.tag; // the tag tells us which text field was tapped
NSString* key = nil;
switch (tag) {
case 1: key = @"lesson"; break;
case 2: key = @"lessonSection"; break;
case 3: key = @"lessonSectionPartFirstWord"; break;
}
// get current value of corresponding instance variable
NSString* curValue = [[self currentCardcontroller].term valueForKey: key];
// ...
A number of built-in Cocoa classes permit you to use key–value coding in a special way. For example:
- If you send valueForKey: to an NSArray, it sends valueForKey: to each of its elements and returns a new array consisting of the results, an elegant shorthand (and a kind of poor man’s map). NSSet behaves similarly.
- NSDictionary implements valueForKey: as an alternative to objectForKey: (useful particularly if you have an NSArray of dictionaries). Similarly, NSMutableDictionary treats setValue:forKey: as a synonym for setObject:forKey:, except that value: can be nil, in which case removeObject:forKey: is called.
- NSSortDescriptor sorts an NSArray by sending valueForKey: to each of its elements. This makes it easy to sort an array of dictionaries on the value of a particular dictionary key, or an array of objects on the value of a particular instance variable.
- CALayer and CAAnimation permit you to use key–value coding to define and retrieve the values for arbitrary keys, as if they were a kind of dictionary; this is useful for attaching identifying and configuration information to one of these instances.
- NSManagedObject, used in conjunction with Core Data, is guaranteed to be key–value coding compliant for attributes you’ve configured in the entity model. Therefore, it’s common to access those attributes with valueForKey: and setValue:forKey:.
文章最后发布于: 2013-11-05 23:19:14