KVC is key-value Coding, while Chinese is key-value Coding. NSKeyValueCoding is a mechanism of informal protocol by NSKeyValueCoding. Objects can access their properties indirectly. This indirect access mechanism complements the direct access provided by instance variables and their associated accessor methods.

useKVC# # # #keyValue and setting value

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

throughkeyPath(Route) value and setting value

// set the nullable id to KeyPath. - (nullable id)valueForKeyPath:(NSString *)key; - (void)setValue (nullable id)value forKeyPath (NSString *) KeyPath;Copy the code

In our usual projects, we mainly use valueForKey and valueForKeyPath to value and set values. Of course, some operations of the collection type can be viewed and used by referring to the documents of apple KVC.

KVCThe setting process of –

SetValue :forKey: This method has a call order (base data type) :

  • Look for the first accessor named set: or _set in order. If found, it is called with the input value (or expanded as needed) and done.
  • If you do not find simple accessor, and class methods accessInstanceVariablesDirectly returns YES, is in order to find name similar to _, _is, or is the instance variables. If found, set the variable directly with the input value (or unpacked value) and finish.
  • When the visitor was not found or instance variables, called setValue: forUndefinedKey:. By default, this raises an exception, but subclasses of NSObject may provide key-specific behavior.

The entire flowchart is as follows (using the name attribute of a Person object as an example) :

KVCValue process

Like the set procedure, the value procedure valueForKey: also has a call order (including collection types) :

  • Search the instance for the first accessor method found with a name like GET, is, or _, 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
  • If no simple accessor method is found, The instance is searched for methods named with the schema countOf and objectInAtIndex (which corresponds to the original method defined by the NSArray class) and AtIndexes (which corresponds to the schema) : NSArray method objectsAtIndexes:). If the first of these and at least one of the other two are found, a collection proxy object is created that responds to all NSArray methods and returns the object. Otherwise, go to Step 3. The proxy object then converts any NSArray message it receives into some combination of countOf, objectInAtIndex: and AtIndexes: messages, and converts it into the key-value encoding compatible object that created it. 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, the proxy object works with the key-value encoding compatible object to allow the underlying property to behave as if it were an NSArray, even though it is not.
  • If no simple accessor method or array access method group is found, look for triplet methods named countOf, enumeratorOf, and memberOf :(corresponding to the original method defined by the NSSet class). If all three methods are found, a collection proxy object is created that responds to all NSSet methods and returns the object. Otherwise, go to Step 4. The proxy object then transforms any NSSet message it receives into some combination of countOf, enumeratorOf, and memberOf: the message to the object that created it. 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.
  • If no simple accessor method or collection access method group is found, and if the recipient’s class method accessInstanceVariables returns YES directly, search for instance variables named _, _is, or is, in that order. If so, get the value of the instance variable and go to Step 5, otherwise go to Step 6.
  • 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.
  • If all other methods fail, call valueForUndefinedKey:. By default, this raises an exception, but subclasses of NSObject may provide key-specific behavior.

The flowchart is as follows:

Custom implementation of KVC

If you implement a KVC you can refer to the above order, implement valueForKey and setValueForKey.

Set the value process

  • Check whether the key is empty. If the key is empty, the value is returned.

  • Check if there are setter methods set

    :, _set

    , setIs

    , implement and return if there are setter methods.

  • If not find whether accessInstanceVariablesDirectly return values to YES, you can go down, or throw an exception.

  • Find out if your ivar list contains instance variables _

    , _is< key>,

    ,is< key>, and assign values if found.

  • If neither is found, an exception is thrown.

The implementation code

- (void) js_setValue: (nullable value forKey: (id) key nsstrings *) {/ / short judgment if (key = = nil | | key. The length = = 0) {return; } // 2: set<Key>: or _set<Key>, NSString *Key = key.capitalizedString; // 2: set<Key>: NSString *Key = key.capitalizedString; NSString *setKey = [NSString stringWithFormat:@"set%@:",Key]; NSString *setKey = [NSString stringWithFormat:@"set%@:",Key]; NSString *_setKey = [NSString stringWithFormat:@"_set%@:",Key]; NSString *setIsKey = [NSString stringWithFormat:@"setIs%@:",Key]; if ([self js_performSelectorWithMethodName:setKey value:value]) { NSLog(@"*********%@**********",setKey); return; }else if ([self js_performSelectorWithMethodName:_setKey value:value]) { NSLog(@"*********%@**********",_setKey); return; }else if ([self js_performSelectorWithMethodName:setIsKey value:value]) { NSLog(@"*********%@**********",setIsKey); return; } / / 3: determine whether response accessInstanceVariablesDirectly returns YES NO melt / / 3: determine whether can direct assignment instance variables if (! [self.class accessInstanceVariablesDirectly] ) { @throw [NSException exceptionWithName:@"JSUnknownKeyException" reason:[NSString stringWithFormat:@"****[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.****",self] userInfo:nil]; NSMutableArray *mArray = [self getIvarListName]; NSMutableArray *mArray = [self getIvarListName]; // _<key> _is<Key> <key> is<Key> NSString *_key = [NSString stringWithFormat:@"_%@",key]; NSString *_isKey = [NSString stringWithFormat:@"_is%@",Key]; NSString *isKey = [NSString stringWithFormat:@"is%@",Key]; If ([mArray containsObject:_key]) {// get ivar ivar ivar = class_getInstanceVariable(self class), _key.UTF8String); // 4.3 Setting the ivar value object_setIvar(self, ivar, value); return; }else if ([mArray containsObject:_isKey]) { Ivar ivar = class_getInstanceVariable([self class], _isKey.UTF8String); object_setIvar(self , ivar, value); return; }else if ([mArray containsObject:key]) { Ivar ivar = class_getInstanceVariable([self class], key.UTF8String); object_setIvar(self , ivar, value); return; }else if ([mArray containsObject:isKey]) { Ivar ivar = class_getInstanceVariable([self class], isKey.UTF8String); object_setIvar(self , ivar, value); return; } @throw [NSException exceptionWithName:@"JSUnknownKeyException" Reason :[NSString stringWithFormat:@"****[%@ %@]: this class is not key value coding-compliant for the key name.****",self,NSStringFromSelector(_cmd)] userInfo:nil]; }Copy the code

Value process

  • Again, the key is not null

  • Search in order: get

    ,

    , countOf

    , objectIn

    AtIndex

  • To determine whether a accessInstanceVariablesDirectly return values to YES and can go down, or throw an exception.

  • Check whether your ivar list contains instance variables _

    , _is< key>,

    ,is< key>.

  • No throw exception was found.

- (nullable id) js_valueForKey: (nsstrings *) key {/ / 1: judgment is not empty if (key = = nil | | key. The length = = 0) {return nil. Get <Key> <Key> countOf<Key> objectIn<Key>AtIndex // Key to uppercase NSString *Key = key.capitalizedString; NSString *getKey = [NSString stringWithFormat:@"get%@",Key]; NSString *getKey = [NSString stringWithFormat:@"get%@",Key]; NSString *countOfKey = [NSString stringWithFormat:@"countOf%@",Key]; NSString *objectInKeyAtIndex = [NSString stringWithFormat:@"objectIn%@AtIndex:",Key]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" if ([self respondsToSelector:NSSelectorFromString(getKey)]) { return [self performSelector:NSSelectorFromString(getKey)]; }else if ([self respondsToSelector:NSSelectorFromString(key)]){ return [self performSelector:NSSelectorFromString(key)];  }else if ([self respondsToSelector:NSSelectorFromString(countOfKey)]){ if ([self respondsToSelector:NSSelectorFromString(objectInKeyAtIndex)]) { int num = (int)[self performSelector:NSSelectorFromString(countOfKey)]; NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1]; for (int i = 0; i<num-1; i++) { num = (int)[self performSelector:NSSelectorFromString(countOfKey)]; } for (int j = 0; j<num; j++) { id objc = [self performSelector:NSSelectorFromString(objectInKeyAtIndex) withObject:@(num)]; [mArray addObject:objc]; } return mArray; }} #pragma clang diagnostic pop // 3: determine whether to directly assign instance variable if (! [self.class accessInstanceVariablesDirectly] ) { @throw [NSException exceptionWithName:@"JSUnknownKeyException" reason:[NSString stringWithFormat:@"****[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.****",self] userInfo:nil]; NSMutableArray *mArray = [self getIvarListName]; // _<key> _is<Key> <key> is<Key> // _name -> _isName -> name -> isName NSString *_key = [NSString stringWithFormat:@"_%@",key]; NSString *_isKey = [NSString stringWithFormat:@"_is%@",Key]; NSString *isKey = [NSString stringWithFormat:@"is%@",Key]; if ([mArray containsObject:_key]) { Ivar ivar = class_getInstanceVariable([self class], _key.UTF8String); return object_getIvar(self, ivar);; }else if ([mArray containsObject:_isKey]) { Ivar ivar = class_getInstanceVariable([self class], _isKey.UTF8String); return object_getIvar(self, ivar);; }else if ([mArray containsObject:key]) { Ivar ivar = class_getInstanceVariable([self class], key.UTF8String); return object_getIvar(self, ivar);; }else if ([mArray containsObject:isKey]) { Ivar ivar = class_getInstanceVariable([self class], isKey.UTF8String); return object_getIvar(self, ivar);; } return @""; }Copy the code

conclusion

In this paper, we mainly explore the process of the value and setting of KVC. The set value procedure set type situation is not written, interested children can see the official Apple documentation to explore.