preface

Key-value coding (KVC) allows developers to access attributes of objects directly (whether or not they are private) through the name of the Key, or to assign values to attributes of objects without invoking explicit access methods. In this way, properties of objects can be accessed and modified dynamically at run time. Not at compile time, but in iOS development, if you want to use KVC, you need the class to comply with the NSKeyValueCoding protocol or the class (directly or indirectly) inherits from the NSObject class, since Apple provides a default implementation of NSKeyValueCoding for the NSObject class.

Learn the key

Regular use of KVC

KVC principle

1. Basic use of KVO

The use of KVC is not very familiar with the KVC can download the basic use of the project to run the project, password: 2SWz.

2. KVC principle

You can refer to the official KVC documentation for an introduction to the PRINCIPLES of KVC

2.1 Value setting process of KVC

In the KVC document, the value process of KVC data attribute mainly consists of three steps, as shown in the figure below:

The translation is roughly as follows:

SetValue :forKey: The default implementation of the method: Taking the given key and value arguments as input, attempts to set the value of a property named key to 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

    :, _set

    , or setIs

    (which is supplementary), 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.

The following is the verification process, first write the following code:

#import <Foundation/ foundation. h> NS_ASSUME_NONNULL_BEGIN @interface TestObject: NSObject - (void)printInstanceVariablesValue; #import "testObject. h" @implementation TestObject {NSString *name; NSString *_name; NSString *_isName; NSString *isName; } - (void)printInstanceVariablesValue { NSLog(@"_name = %@ -- _isName = %@ -- name = %@ -- isName = %@", self->_name, self->_isName, self->name, self->isName); } + (BOOL)accessInstanceVariablesDirectly { return NO; } - (void)setValue:(id)value forUndefinedKey:(NSString *)key { NSLog(@"%s, key = %@, value = %@", __func__, key, value); } - (void)setName:(NSString *)name { NSLog(@"%s, %@", __func__, name); } - (void)_setName:(NSString *)name { NSLog(@"%s, %@", __func__, name); } - (void)setIsName:(NSString *)name { NSLog(@"%s, %@", __func__, name); } - (void)_setIsName:(NSString *)name { NSLog(@"%s, %@", __func__, name); } @implementation ViewController - (void)viewDidLoad {[super viewDidLoad]; [self setValuefundamentalTest]; } - (void)setValuefundamentalTest { TestObject *testObj = [[TestObject alloc] init]; [testObj setValue:@" ha-ha-ha "forKey:@"name"]; [testObj printInstanceVariablesValue]; }Copy the code

Run the program, and the console outputs the printed results as shown below:

Then comment out the setName method in the testObject. m file code, run the program, and the console outputs the printed results as shown in the figure below:

Then comment out the _setName method in the testObject. m file code, run the program, and the console outputs the printed results as shown in the figure below:

Then comment out the setIsName method in the testObject. m file code, run the program, and the console outputs the printed result as shown below:

Can be found that is will not then call _setIsName method, but invoked the setValue: forUndefinedKey: method, and method call priority order: set the < Key > :, _set < Key >, set the < IsKey >.

Then set method above all commented, accessInstanceVariablesDirectly method’s return value to YES, execute the code, the console output information as shown below:

Then comment out the instance variable _name in testObject. m, execute the code, and the console output is as follows:

Then comment out the instance variable _isName in testObject.m, execute the code, and the console output looks like this:

Then comment out the instance variable name in testObject. m and execute the code. The console output looks like this:

Finally, comment out the instance variable isName in testObject. m, execute the code, and the console output information is as follows:

You can see that the instance variables of the object are set in the order of precedence: _name, _isName, name, isName, if not found above four instance variables, is called setValue: forUndefinedKey: method, if setValue: forUndefinedKey: method all have no, the correct will print out the abnormal information, As follows:

2.2 KVC Value process

The process of KVC value in the KVC document mainly consists of six steps, as shown in the following figure:

ValueForKey: The default implementation of the method. Given a key argument as input, perform the following steps to perform internal operations from the time the instance object of the class receives a valueForKey message.

  1. Search the instance object for an accessor method named GET

    ,

    , is

    , or _< Key>. If it is found, call it, get the return of the method and perform Step 5. Otherwise, perform the next step.


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

    , objectIn

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

    atIndexes (corresponding to the NSArray method obejctsAtIndexes :), 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, perform Step 3. The proxy object then converts all the 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 called 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 above are found, create a collection proxy object that responds to all NSSet methods and returns it. Otherwise, go to Step 4. The proxy object then converts any NSSet messages it receives into a combination of countOf

    , enumeratorOf

    , memberOf

    : ‘messages, and into 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 accessinstancevariablesdirect returns YES if the receiver class method, the search for instance variables in turn _ < key >, _is < key >, < the key > or is < key >, if found, Get the value of the instance variable directly, 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:, which by default raises an exception, but subclasses of NSObject may provide key-specific behavior.

The exploration validation process is as follows (this article only explores instance variables of non-collection types) :

Write the following code:

#import <Foundation/ foundation. h> NS_ASSUME_NONNULL_BEGIN @interface TestObject: NSObject - (void)setInstanceVariablesValue; #import "testObject. h" @implementation TestObject {NSString *name; NSString *_name; NSString *_isName; NSString *isName; } - (void)setInstanceVariablesValue { self->name = @"name"; self->_name = @"_name"; self->_isName = @"_isName"; self->isName = @"isName"; } + (BOOL)accessInstanceVariablesDirectly { return NO; } - (NSString *)getName { NSLog(@"%s", __func__); Return @" whoop "; } - (NSString *)name { NSLog(@"%s", __func__); Return @" hee hee "; } - (NSString *)getIsName { NSLog(@"%s", __func__); Return @" whoop "; } - (NSString *)isName { NSLog(@"%s", __func__); Return @" squeaks "; } - (NSString *)get_Name { NSLog(@"%s", __func__); Return @" whoop "; } - (NSString *)_name { NSLog(@"%s", __func__); Return @" squeaks "; } - (NSString *)get_isName { NSLog(@"%s", __func__); Return @" whoop "; } - (NSString *)_isName { NSLog(@"%s", __func__); Return @" squeaks "; } - (id)valueForUndefinedKey:(NSString *)key { NSLog(@"key = %@", key); return @""; } @implementation ViewController - (void)viewDidLoad {[super viewDidLoad]; [self getValuefundamentalTest]; } - (void)getValuefundamentalTest { TestObject *testObj = [[TestObject alloc] init]; [testObj setInstanceVariablesValue]; NSString *name = [testObj valueForKey:@"name"]; NSLog(@"%@", name); }Copy the code

Run the program, and the console prints the following information:

Comment out the getName method in the testObject. m file and run the program. The console prints the following information:

Comment out the name method in the testObject. m file and run the program. The console prints the following information:

Comment out the isName method in the testObject. m file and run the program. The console prints the following information:

Comment out the _name method in the testObject. m file and run the program. The console prints the following information:

It can be found that the priority call order of get method of instance variable name is: GetName, name, isName, _name, then all the methods above comments, then accessInstanceVariablesDirectly function return values set to YES, run the program, the console print information as shown below:

Take instance variables_nameComment out, run the program, the console prints the following information:

Comment out the instance variable _isName, run the program, and the console prints the following information:

Comment out the instance variable name, run the program, and the console prints the following information:

Comment out the instance variable isName and run the program. The console prints the following information:

If valueForUndefinedKey is commented out again, the program will crash, as shown below:

As you can see, the order of obtaining instance variables in the object is _name, _isName, name, isName.