About key-value encoding

Key-value coding (KVC)

Key-value encoding is a mechanism enabled by the NSKeyValueCoding informal protocol that an object uses to provide indirect access to its properties. When an object conforms to key-value encoding, its properties can be addressed through a concise, unified messaging interface using string arguments. This indirect access mechanism complements the direct access provided by instance variables and their associated accessor methods.

You typically use accessor methods to access the properties of an object. The get accessor (or getter) returns the value of the property. A set accessor (or setter) sets the value of a property. In Objective-C, you can also directly access the underlying instance variable of a property. Accessing object properties in any of these ways is simple, but requires calling a method or variable name specific to the property. As the list of properties grows or changes, so must the code that accesses those properties. In contrast, key-value encoding objects provide a simple messaging interface that is consistent across all of its properties.

Key-value encoding is a basic concept that underlies many other Cocoa technologies, such as key-value observing, Cocoa Bindings, Core Data, and Applescript-ability. In some cases, key-value coding can also help simplify the code.

Encode compatible objects using key values

Objects are usually key-value encoded in NSObject (direct or indirect) inheritance, both of which adopt the NSKeyValueCoding protocol and provide a default implementation for the base method. Such objects enable other objects to perform the following operations through a compact messaging interface:

  • Access object properties. This protocol specifies methods, such as getter valueForKey: and setter setValue:forKey:, that are used to access object properties by name or key, taking strings as arguments. The default implementation of these and related methods uses keys to locate and interact with the underlying data, such as the Accessing Object Properties.
  • Manipulate collection properties.Access the default implementation of the method and the collection properties of the object (e.gNSArrayObject, and any other property. In addition, if an object defines a collection accessor method for a property, it allows key-value access to the contents of the collection. This is often more efficient than direct access, and allows you to use custom collection objects through a standardized interface, such asAccessing Collection Properties.
  • Calls the collection operator on the collection object.When a collection property is accessed in an object that matches the key-value encoding, theSet operatorInsert into the key string, as inUsing Collection Operators. Set operators are based on the defaultNSKeyValueCodingA getter implementation performs an operation on a collection and then returns a new filtered version of the collection or a single value representing some characteristic of the collection.
  //@sum: sum of values
  //@avg: average value
  //@count: total number
  //@max: maximum value
  //@min: minimum value
  
   Person * p1 = [[Person alloc]init];
    Person * p2 = [[Person alloc]init];
    Person * p3 = [[Person alloc]init];
    
    p1->age = 18;
    p2->age = 30;
    p3->age = 56;
   
   NSArray * array = @[p1,p2,p3];
   NSNumber* sum = [array valueForKeyPath:@"@sum.age"];
   NSNumber* avg = [array valueForKeyPath:@"@avg.age"];
   NSNumber* count = [array valueForKeyPath:@"@count"];
   NSNumber* min = [array valueForKeyPath:@"@min.age"];
   NSNumber* max = [array valueForKeyPath:@"@max.age"];
   
   NSLog(@"sum:%@, avg:%@, count:%@, min:%@, max:%@",sum,avg,count,min,max);
   
   Sum :104, avg: 34.66666666666666666666, count:3, min:18, Max :56
Copy the code
  • Access non-object properties.The default implementation of the protocol detects non-object properties, including scalars and structures, and automatically wraps and unwraps them into objects used on the protocol interface, such asRepresenting Non-Object Values. In addition, the protocol declares a method that allows compatible objectsnilProvides appropriate action for this case when setting values on non-object properties through the key-value encoding interface.
  • Key PATH Access attribute. If you have a hierarchy of objects that match key-value encoding, you can use method calls based on key Path, using a single call to drill down into the hierarchy and get or set values.

The key-value encoding of the object is adopted

To make your own object key-value encodings work, you need to ensure that they adopt the NSKeyValueCoding informal protocol and implement corresponding methods, such as as valueForKey: generic getter and setValue:forKey: generic setter. Fortunately, as mentioned above, NSObject takes this protocol and provides default implementations for these and other basic methods. So, if you derive an object from NSObject (or any of its many subclasses), then most of the work is done.

For the default method to do its job, you need to ensure that the object’s accessor methods and instance variables follow some clearly defined pattern. This allows the default implementation to find the properties of the object in response to the key-value encoded message. You can then choose to extend and customize the key-value encoding by providing validation methods and handling special cases.

Above is a translation from the author: codeTao source

The search mode of the underlying Getter

  1. The first accessor method named get

    ,

    , is

    , or _< Key> is found in the instance search, in that order. If it is found, it is invoked and the results are used to proceed to Step 5. Otherwise proceed to the next step.

  2. If no simple accessor method is found, The instance is searched for methods with names and modes countOf

    and objectIn

    AtIndex :(corresponding to the original method defined by the NSArray class) and

    AtIndexes :(corresponding to the NSArray method objectsAtIndexes :).

    If the first of these and at least one of the other two are found, a collection proxy object that responds to all NSArray methods is created and returned. Otherwise, go to Step 3.

    The proxy object then sends any NSArray some combination of messages countOf

    , objectIn

    AtIndex: and

    AtIndexes: messages to key-value encoding to create its compatible objects. If the original object also implements an optional method with a name like get

    :range:, then the proxy object will also use it when appropriate. In fact, proxy objects that work with key-value encoding compatible objects allow the underlying property to behave as if it were an NSArray, even though it is not.

  3. If no simple access method or array access method group is found, look for a triple method named countOf

    , enumeratorOf

    and memberOf

    :(corresponding to the original method NSSet class defined by).

    If all three methods are found, a collection proxy object that responds to all NSSet methods is created and returned. Otherwise, go to Step 4.

    This proxy object then takes some combination of countOf

    , enumeratorOf

    and memberOf

    : messages received by any NSSet to create its object. In fact, proxy objects that work with key-value encoding compatible objects allow the underlying property to behave as if it were an NSSet, even though it is not.

  4. If found to collect the access methods simple access method or group, accessInstanceVariablesDirectly returns YES if the receiver class method, search called instance variables _ < key >, _is < key >, < the key >, or is < key >, in this order. If so, get the value of the instance variable and go to Step 5, otherwise go to Step 6.

  5. If the retrieved property value is an object pointer, simply return the result.

    If the value is of the supported scalar type NSNumber, store it in an instance of NSNumber and return that instance.

    If the result is a scalar type not supported by NSNumber, it is converted to an NSValue object and returned.

  6. If all other methods fail, call valueForUndefinedKey:. By default, this raises an exception, but the NSObject subclass of NSObject might provide key-specific behavior.

The search pattern for the basic Setter

The default implementation of setValue:forKey:, given the key and value parameters as input, attempts to set the named attribute key to value (or, for non-object properties, the expanded version of value, such as in the case of non-object values) to receive a call within an object, using the following program:

  1. Find the name in orderset<Key>:The first accessor of or_set<Key>As well assetIs<Key>:. If found, it is called with the input value (or expanded as needed) and done.
  2. If no simple access is found, if the class methodaccessInstanceVariablesDirectlyreturnYES, looking for an instance variable similar to the name_<key>._is<Key>.<key>Or,is<Key>In that order. If found, set the variable directly with the input value (or unpacked value) and finish.
  3. Call after an accessor or instance variable cannot be foundsetValue:forUndefinedKey:By default, this throws an exception, butNSObjectSubclasses of may provide key-specific behavior.
    Person * p1 = [[Person alloc]init];
    [p1 setValue:@"Kowaii" forKey:@"name"];
    NSLog(@"value = %@",[p1 valueForKey:@"_name"]);
    
// NSLog(@"Person %@-%@-%@-%@",p1->_name,p1->name,p1->_isName,p1->isName);
    NSLog(@"Person %@--%@-%@",p1->name,p1->_isName,p1->isName);
Copy the code

/ / person code

//- (void)setName:(NSString*)name{
// NSLog(@"func %s name = %@",__FUNCTION__,name);
/ /}
//
//- (void)_setName:(NSString*)name{
// NSLog(@"func %s name = %@",__FUNCTION__,name);
/ /}

- (void)setIsName:(NSString*)name{
    NSLog(@"func %s name = %@",__FUNCTION__,name);
}
Copy the code

Set

: and _set

ObjectForKey, valueforKey and valueForKeyPath

  1. ValueForKey: and valueAtKeyPath: are both methods that are specified by the informal protocol of NSKeyValueCoding, and are implemented by default in NSObject

    ObjectForKey: is the method of the dictionary

  2. ValueForKey: only one level can be indexed, while valueAtKeyPath: multiple levels can be indexed, for example

NSDictionary *dict1 = @{@"dic1": @ {@"dic2": @ {@"name": @"lisi"The @"info": @ {@"age": @"111"}}}};

id res = [dict1 valueForKeyPath:@"[dict1.dict2.name]"];
Copy the code

ValueForKey: It can traverse multiple objects. So you can have a root object with a bunch of properties, each of which is another object with another set of properties, and so on, and using a key path you can retrieve a value on the leaf of that data structure, instead of going through object after object for yourself. You can use the manipulation of collection properties to call collection operators on collection objects. And so on unique SAO operation, and want to SAO to explore their own

    NSArray *arr = @[@{@"city": @"beijing"The @"person": @ {@"name": @"Library fine"}}, @ {@"city": @"shenzhen"
                         
                     }];
    NSLog(@"% @",[arr valueForKeyPath:@"city"]);
    
   Szechwan / / print (Beijing, chengdu)

Copy the code