「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战」。
二、KVC 设值 底层原理
针对赋值,Key-Value Coding Programming Guide中有这样一段
Search Pattern for the Basic Setter
The default implementation of setValue:forKey:, given key and value parameters as input, attempts to set a property named key to value (or, for non-object properties, the unwrapped version of value, as described in Representing Non-Object Values) inside the object receiving the call, using the following procedure:
Look for the first accessor named set<Key>: or _set<Key>, in that order. If found, invoke it with the input value (or unwrapped value, as needed) and finish.
If no simple accessor is found, and if the class method accessInstanceVariablesDirectly returns YES, look for an instance variable with a name like _<key>, _is<Key>, <key>, or is<Key>, in that order. If found, set the variable directly with the input value (or unwrapped value) and finish.
Upon finding no accessor or instance variable, invoke setValue:forUndefinedKey:. This raises an exception by default, but a subclass of NSObject may provide key-specific behavior.
对于setValue:forKey:给定的key和value参数作为输入,尝试设置命名属性key到value主要流程为
- 按顺序查找名为set或 _set方法,如果找到,直接调用。如果没找到,则第2步
- 调用类方法accessInstanceVariablesDirectly 如果返回Yes,则进入间接访问实例化赋值,寻找名为_,_is,,或者is的变量,如果找到,则直接使用输入值设置变量。
- 如果返回NO或者没找到上面的值,则调用setValue:forUndefinedKey:,默认会抛出异常。
举个例子:
LGPerson *person = [[LGPerson alloc] init];
// 1、一般setter 方法
person.name = @"AA";
// 2、KVC方式
[person setValue:@"CC" forKey:@"name"];
流程大概如下图:
二、KVC 取值 底层原理
针对赋值,Key-Value Coding Programming Guide中有这样一段
Accessor Search Patterns
The default implementation of the NSKeyValueCoding protocol provided by NSObject maps key-based accessor calls to an object’s underlying properties using a clearly defined set of rules. These protocol methods use a key parameter to search their own object instance for accessors, instance variables, and related methods that follow certain naming conventions. Although you rarely modify this default search, it can be helpful to understand how it works, both for tracing the behavior of key-value coded objects, and for making your own objects compliant.
NOTE
The descriptions in this section use <key> or <Key> as a placeholder for the key string that appears as a parameter in one of the key-value coding protocol methods, which is then used by that method as part of a secondary method call or variable name lookup. The mapped property name obeys the placeholder’s case. For example, for the getters <key> and is<Key>, the property named hidden maps to hidden and isHidden.
Search Pattern for the Basic Getter
The default implementation of valueForKey:, given a key parameter as input, carries out the following procedure, operating from within the class instance receiving the valueForKey: call.
Search the instance for the first accessor method found with a name like get<Key>, <key>, is<Key>, or _<key>, in that order. If found, invoke it and proceed to step 5 with the result. Otherwise proceed to the next step.
If no simple accessor method is found, search the instance for methods whose names match the patterns countOf<Key> and objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSArray class) and <key>AtIndexes: (corresponding to the NSArray method objectsAtIndexes:).
If the first of these and at least one of the other two is found, create a collection proxy object that responds to all NSArray methods and return that. Otherwise, proceed to step 3.
The proxy object subsequently converts any NSArray messages it receives to some combination of countOf<Key>, objectIn<Key>AtIndex:, and <key>AtIndexes: messages to the key-value coding compliant object that created it. If the original object also implements an optional method with a name like get<Key>:range:, the proxy object uses that as well, when appropriate. In effect, the proxy object working together with the key-value coding compliant object allows the underlying property to behave as if it were an NSArray, even if it is not.
If no simple accessor method or group of array access methods is found, look for a triple of methods named countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: (corresponding to the primitive methods defined by the NSSet class).
If all three methods are found, create a collection proxy object that responds to all NSSet methods and return that. Otherwise, proceed to step 4.
This proxy object subsequently converts any NSSet message it receives into some combination of countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: messages to the object that created it. In effect, the proxy object working together with the key-value coding compliant object allows the underlying property to behave as if it were an NSSet, even if it is not.
If no simple accessor method or group of collection access methods is found, and if the receiver's class method accessInstanceVariablesDirectly returns YES, search for an instance variable named _<key>, _is<Key>, <key>, or is<Key>, in that order. If found, directly obtain the value of the instance variable and proceed to step 5. Otherwise, proceed 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, store it in an NSNumber instance and return that.
If the result is a scalar type not supported by NSNumber, convert to an NSValue object and return that.
If all else fails, invoke valueForUndefinedKey:. This raises an exception by default, but a subclass of NSObject may provide key-specific behavior.
对于取值,文档中有以上说明,其实类似于上面的赋值原理。
在valueForKey:给定key参数作为输入的情况下
- 首先查找getter方法,按照get -> -> is -> _的方法顺序查找 如果找到则进入步骤5,如果没找到,则下一步
- 如果步骤1中的getter方法没有找到,KVC会查找countOf 和objectIn AtIndex :和 AtIndexes : 如果找到其中的第一个以及其他两个中的至少一个,则创建一个响应所有NSArray方法的集合代理对象,并返回该对象。否则,请继续执行步骤3。代理对象随后将任何NSArray接收到的一些组合的消息countOf,objectInAtIndex:和AtIndexes:消息给KVC创建它兼容的对象。如果原始对象还实现了名称为的可选方法get:range:,则代理对象也会在适当时使用该方法。
- 如果没有找到简单的访问方法或阵列访问方法组,寻找一个三重的方法命名countOf,enumeratorOf和memberOf:(对应于由所定义的原始的方法NSSet类)。如果找到所有三个方法,请创建一个响应所有NSSet方法的集合代理对象并将其返回。否则,请继续执行步骤4。
- 如果此时还未找到,且接收器的类方法accessInstanceVariablesDirectly返回YES,搜索名为实例变量_,_is,,或者is,按照这个顺序。如果找到,请直接获取实例变量的值,然后继续执行步骤5。否则,请继续执行步骤6。
- 如果检索到的属性值是对象指针,则只需返回结果。
如果该值是所支持的标量类型NSNumber,则将其存储在NSNumber实例中并返回该实例。
如果结果是NSNumber不支持的标量类型,请转换为NSValue对象并返回该对象。- 如果其他所有方法均失败,请调用valueForUndefinedKey:。默认情况下会引发异常
流程大概如下图: