KVC profileThe official documentation

  • Key-Value Coding

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

The use of KVC

KVC is often used in development, such as assigning object attributes, adding User Defined Runtime view attributes to StoryBoard, etc

- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
Copy the code

1. KVC – Basic type

LGPerson *person = [[LGPerson alloc] init];
person.name      = @"17"; 
person.age       = 18;
person->myName   = @"19";
NSLog(@"%@ - %d - %@",person.name,person.age,person->myName);
[person setValue:@"16" forKey:@"name"];
NSLog(@"%@ - %d - %@",person.name,person.age,person->myName);
Copy the code

Results:

2. Kvc-collection type

  • Methods a
person.array = @[@"1",@"2",@"3"];
NSLog(@"%@",[person valueForKey:@"array"]);
NSArray *array = [person valueForKey:@"array"];
array = @[@"100",@"2",@"3"];
[person setValue:array forKey:@"array"];
NSLog(@"%@",[person valueForKey:@"array"]);
Copy the code

Results:

  • Method 2
person.array = @[@"1",@"2",@"3"];
NSMutableArray *mArray = [person mutableArrayValueForKey:@"array"];
mArray[0] = @"200";
NSLog(@"%@",[person valueForKey:@"array"]);
Copy the code

Results:

3. Kvc-set operator

  • Aggregate operator
// @avg, @count, @max, @min, @sum - (void)aggregationOperator{NSMutableArray *personArray = [NSMutableArray array]; for (int i = 0; i < 6; i++) { LGStudent *p = [LGStudent new]; NSDictionary* dict = @{ @"name":@"Tom", @"age":@(18+i), @"nick":@"Cat", @"length":@(175 + 2*arc4random_uniform(6)), }; [p setValuesForKeysWithDictionary:dict]; [personArray addObject:p]; } NSLog(@"%@", [personArray valueForKey:@"length"]); Float avg = [[personArray valueForKeyPath:@"@avg. Length "] floatValue]; NSLog(@"%f", avg); int count = [[personArray valueForKeyPath:@"@count.length"] intValue]; NSLog(@"%d", count); int sum = [[personArray valueForKeyPath:@"@sum.length"] intValue]; NSLog(@"%d", sum); int max = [[personArray valueForKeyPath:@"@max.length"] intValue]; NSLog(@"%d", max); int min = [[personArray valueForKeyPath:@"@min.length"] intValue]; NSLog(@"%d", min); }Copy the code
  • Array operator
@distinctUnionofObjects @unionofObjects - (void)arrayOperator{NSMutableArray *personArray = [NSMutableArray array]; for (int i = 0; i < 6; i++) { LGStudent *p = [LGStudent new]; NSDictionary* dict = @{ @"name":@"Tom", @"age":@(18+i), @"nick":@"Cat", @"length":@(175 + 2*arc4random_uniform(6)), }; [p setValuesForKeysWithDictionary:dict]; [personArray addObject:p]; } NSLog(@"%@", [personArray valueForKey:@"length"]); NSArray* arr1 = [personArray valueForKeyPath:@" @unionofObjects.length "]; NSLog(@"arr1 = %@", arr1); / / return a collection of objects specified attribute to heavy NSArray * arr2 = [personArray valueForKeyPath: @ "@ distinctUnionOfObjects. Length"]. NSLog(@"arr2 = %@", arr2) }Copy the code
  • The nested collection operator
@distinctUnionofArrays @unionofArrays @distinctUnionofSets - (void)arrayNesting{NSMutableArray *personArray1 = [NSMutableArray array]; for (int i = 0; i < 6; i++) { LGStudent *student = [LGStudent new]; NSDictionary* dict = @{ @"name":@"Tom", @"age":@(18+i), @"nick":@"Cat", @"length":@(175 + 2*arc4random_uniform(6)), }; [student setValuesForKeysWithDictionary:dict]; [personArray1 addObject:student]; } NSMutableArray *personArray2 = [NSMutableArray array]; for (int i = 0; i < 6; i++) { LGPerson *person = [LGPerson new]; NSDictionary* dict = @{ @"name":@"Tom", @"age":@(18+i), @"nick":@"Cat", @"length":@(175 + 2*arc4random_uniform(6)), }; [person setValuesForKeysWithDictionary:dict]; [personArray2 addObject:person]; } NSArray* nestArr = @[personArray1, personArray2]; NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.length"]; NSLog(@"arr = %@", arr); NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.length"]; NSLog(@"arr1 = %@", arr1); }Copy the code

4. KVC – Access non-object attributes

ThreeFloats floats = {1.,2.,3.};
NSValue *value     = [NSValue valueWithBytes:&floats objCType: @encode(ThreeFloats)];
[person setValue:value forKey:@"threeFloats"];
NSValue *value1    = [person valueForKey:@"threeFloats"];
NSLog(@"%@",value1);
ThreeFloats th;
[value1 getValue:&th];
NSLog(@"%f-%f-%f",th.x,th.y,th.z);
Copy the code

Results:

5.:KVC – Layer access – keyPath

LGStudent *student = [LGStudent alloc];
student.subject    = @"1234";
person.student     = student;
[person setValue:@"Swift" forKeyPath:@"student.subject"];
NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);
Copy the code

Results:

KVC sets the value and evaluates the processThe official documentation

Set the value principle

The search module for the basic Setter

The default implementation of setValue:forKey: attempts to set a property named key as value(or, for non-object properties, an unwrapped version of value, as represented by non-object values) inside the object receiving the call, using the following procedure:

  1. Find the first accessor named set

    : or _set

    , in that order. If it is found, it is called with the input value (or unwrapped value as needed) and done.

  2. If you don’t find a simple accessor, and if the class methods accessinstancevariablesdirect returns YES, please find an instance variable, the name of _ < key >, _is < key >, < the key >, or is < key >. If found, set the variable directly with the input value (or unwrapped value) and finish.

  3. When had not found the accessor or instance variables, called setValue: forUndefinedKey:. By default, this throws an exception, but subclasses of NSObject may provide key-specific behavior.

Set value flow chart

The principle of value

Search mode for basic getters

The default implementation of valueForKey:, given a key argument as input, performs the following operations from within the class instance that receives valueForKey: Call.

  1. Search the instance for accessor methods whose first name is GET

    ,

    , is

    , or _< Key>. If it is found, it is called and the results are used to proceed to Step 5. Otherwise, go to the next step.


  2. If no simple accessor method is found, Search the instance for the methods of matching patterns countOf

    and objectIn

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

    AtIndexes (corresponding to the objectsAtIndexes:) of the NSArray method. If the first and at least one of the other two are found, create a collection proxy object that responds to all NSArray methods and returns it. Otherwise, go to Step 3. The proxy object then converts all NSArray messages it receives into a combination of countOf

    , objectIn

    AtIndex:, and

    AtIndexes: messages and into the key-value encoded object that created it. If the original object also implements an optional method named GET

    :range:, the proxy object uses that method when appropriate. In effect, the proxy object works with an object that conforms to the key-value encoding, allowing the underlying property to behave like NSArray even if it is not.






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

    , enumeratorOf

    , and memberOf

    (corresponding to the base method defined by the NSSet class). If all three methods are found, create a collection proxy object that responds to all NSSet methods and returns it. Otherwise, go to Step 4. This proxy object then converts any NSSet messages it receives to a combination of countOf

    , enumeratorOf

    , memberOf

    : Messages, and to the object that created it. In effect, the proxy object works with objects that conform to the key-value encoding, allowing the underlying property to behave as if it were an NSSet, even though it is not.





  4. If you don’t find a simple way to access or set a set of access methods, and if the recipient’s class methods accessinstancevariablesdirect returns YES, search instance variables _ < key >, _is < key >, < the key >, or is < key >, in turn. If found, simply get the value of the instance variable and proceed 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 a scalar type supported by NSNumber, it is stored in an NSNumber instance and returned. 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 throws an exception, but subclasses of NSObject may provide key-specific behavior.

Value flow chart

KVC exception handling

If there is no key or keyPath (e.g. dictionary value, object assignment), NSUnknownKeyException will be thrown and the program will Crash

Set value of Crash

LGPerson *p = [[LGPerson alloc] init];
[p setValue:@"1" forKey:@"name111"];
Copy the code

Results:

Set value of null

LGPerson *p = [[LGPerson alloc] init];
NSString *string = nil;
[p setValue:string forKey:@"name"];
NSLog(@"%@",[p valueForKey:@"name"]);
Copy the code

Results:

Value of Crash

LGPerson *p = [[LGPerson alloc] init];
[p valueForKey:@"name111"];
Copy the code

Results:

Exception handling

Apple provides corresponding exception handling methods

/* Given that an invocation of -valueForKey: would be unable to get a keyed value using its default access mechanism, return the keyed value using some other mechanism. The default implementation of this method raises an NSUndefinedKeyException. You can override it to handle properties that are dynamically defined at run-time.*/

- (**nullable** **id**)valueForUndefinedKey:(NSString *)key;

/* Given that an invocation of -setValue:forKey: would be unable to set the keyed value using its default mechanism, set the keyed value using some other mechanism. The default implementation of this method raises an NSUndefinedKeyException. You can override it to handle properties that are dynamically defined at run-time.*/

- (**void**)setValue:(**nullable** **id**)value forUndefinedKey:(NSString *)key;

/* Given that an invocation of -setValue:forKey: would be unable to set the keyed value because the type of the parameter of the corresponding accessor method is an NSNumber scalar type or NSValue structure type but the value is nil, set the keyed value using some other mechanism. The default implementation of this method raises an NSInvalidArgumentException. You can override it to map nil values to something meaningful in the context of your application.*/

- (**void**)setNilValueForKey:(NSString *)key;
Copy the code

Recorresponding methods in the target class can avoid crashes, but this can be tedious and can be added to a unified parent class or added classification