KVC底层原理

KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。

KVC在iOS中的定义

无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-C中有个显式的NSKeyValueCoding类别名,而Swift没有,也不需要)。所以对于所有继承了NSObject的类型,也就是几乎所有的Objective-C对象都能使用KVC(一些纯Swift类和结构体是不支持KVC的),下面是KVC最为重要的四个方法

KVC底层原理_第1张图片
2.png
3.png

//默认返回YES,表示如果没有找到Set方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索

+ (BOOL)accessInstanceVariablesDirectly;

//KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。

- (BOOL)validateValue:(inoutid__nullable* __nonnull)ioValue forKey:(NSString*)inKey error:(outNSError**)outError;

//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。

- (NSMutableArray*)mutableArrayValueForKey:(NSString*)key;

//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。

- (nullableid)valueForUndefinedKey:(NSString*)key;

//和上一个方法一样,但这个方法是设值。

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

//如果你在SetValue方法时面给Value传nil,则会调用这个方法

- (void)setNilValueForKey:(NSString*)key;

//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。

- (NSDictionary *)dictionaryWithValuesForKeys:(NSArray *)keys;

iOS成员变量、实例变量、属性区别?


KVC底层原理_第2张图片
1.png

实例变量:由是实例创建的变量,是一种特殊的成员变量。

id 是一种特殊的class,是OC对象,

属性有默认的setter、getter,这是怎么来的?苹果早期编译器是GCC,后面变成了LLVM,LLVM如果发现一个实例变量,没有匹配到实例变量的属性的时候,它就会自动创建一个带下划线的变量名,同时会给你添加setter、getter方法,以前我们还需要一个函数@synthesize userName = _userName,它会自动生成setter、getter,它的作用是啥?第一步 自动帮你生成 setter、getter方法,第二部 指定属性对应的实例变量。

KeyVaue-Coding官方文档地址

KVC的取值过程

取值过程的官方文档截图

KVC底层原理_第3张图片
取值1.png
KVC底层原理_第4张图片
取值2.png

画的流程图


KVC底层原理_第5张图片
取值的流程图3.png

代码示例:


KVC底层原理_第6张图片
代码1.png
KVC底层原理_第7张图片
代码2.png
KVC底层原理_第8张图片
代码3.png

最后输出结果为 _name

这是为啥呢? 看流程图1 没有找到对应的 方法,然后LGPerson里面的accessInstanceVariablesDirectly返回的为yes,按照流程图的第4步 直接去查找_name,有直接赋值, 所以name的值为_name


KVC底层原理_第9张图片
数组get1.png
KVC底层原理_第10张图片
数组get2.png
KVC底层原理_第11张图片
数组get3.png
KVC底层原理_第12张图片
数组get4.png

如果 LGPerson 没有写 array get相关 就会崩溃,按照 get流程的步骤2 实现了 objectInPensAtIndex 就会直接取值,不会崩溃了


KVC底层原理_第13张图片
set1.png
KVC底层原理_第14张图片
set2.png
KVC底层原理_第15张图片
set3.png
KVC底层原理_第16张图片
set4.png

set取值的操作,按照取值步骤3,最终输出结果 如图set4.png


KVC的赋值过程

赋值过程的官方文档截图


KVC底层原理_第17张图片
putong1.png
KVC底层原理_第18张图片
array1.png
KVC底层原理_第19张图片
array2.png
KVC底层原理_第20张图片
array3.png
KVC底层原理_第21张图片
set1.png
KVC底层原理_第22张图片
set2.png
KVC底层原理_第23张图片
set3.png
KVC底层原理_第24张图片
set4.png

变量setter的流程


KVC底层原理_第25张图片
变量setter.png

上面流程测试

KVC底层原理_第26张图片
accessNO-测试1.png


KVC底层原理_第27张图片
accessNO-测试2.png
KVC底层原理_第28张图片
accessNO-测试3.png
accessNO-测试5.png


KVC底层原理_第29张图片
accessYES-测试1.png


KVC底层原理_第30张图片
accessYES-测试2.png


accessYES-测试3.png

数组赋值

当对象的属性是可变的容器时,对于有序的容器,可以用下面的方法:


array1.png


KVC底层原理_第31张图片
array2.png

代码测试


KVC底层原理_第32张图片
add1.png
add2.png


KVC底层原理_第33张图片
inser1.png
inser2.png


KVC底层原理_第34张图片
all1.png
KVC底层原理_第35张图片
all2.png
KVC底层原理_第36张图片
person1.png
KVC底层原理_第37张图片
person2.png
KVC底层原理_第38张图片
person3.png


KVC底层原理_第39张图片
arrayset1.png
KVC底层原理_第40张图片
arrayset2.png
KVC底层原理_第41张图片
arrayset3.png
KVC底层原理_第42张图片
arrayset4.png

NSMutableSet的set过程


KVC底层原理_第43张图片
set.png

可以看出 set 和 array 的取值方式 差不多,这里不做代码演示了。

KVC的正确性验证


check.png

这个方法的默认实现是去探索类里面是否有一个这样的方法:-(BOOL)validate:error:如果有这个方法,就调用这个方法来返回,没有的话就直接返回YES


KVC底层原理_第44张图片
vc.png
KVC底层原理_第45张图片
person.png
log.png

自定义KVC逻辑

set值简单流程


KVC底层原理_第46张图片
set值简单流程.png

get值简单流程


KVC底层原理_第47张图片
get.png

自定义KVC代码

利用KVC处理异常

1.找不到key,setvalue:forkey 没这个key


KVC底层原理_第48张图片
异常1.png

2.传值为nil


KVC底层原理_第49张图片
异常2.png
异常3.png

name属性是对象,所以赋值为nil不会崩溃,对象类型可以为nil;但是age是整数,整数的类型不会是nil,这么强行赋值就会抛出异常出现错误


KVC底层原理_第50张图片
异常4.png

重写了这个方法就不会崩溃和出错了

KVC底层原理_第51张图片
异常5.png

KVC进阶用法

keyPath中的集合运算符的使用

①简单集合运算符
简单集合运算符共有

@avg 首先把集合中的每个对象都转换为double类型,然后计算其平均值,最后返回一个值为该平均值的NSNumber对象

 @count 返回一个值为集合中对象总数的NSNumber对象

@max 使用compare:方法来确定最大值。所以为了让其正常工作,集合中所有的对象都必须支持和另一个对象的比较

@min 和@max一样,但是返回的是集合中的最小值。

@sum 首先把集合中的每个对象都转换为double类型,然后计算其总,最后返回一个值为这个总和的NSNumber对象

KVC底层原理_第52张图片
计算1.png
KVC底层原理_第53张图片
计算2.png

对象操作符

NSArray*inventory = @[iPhone5, iPhone5, iPhone5, iPadMini, macBookPro, macBookPro];

@unionOfObjects/ @distinctUnionOfObjects: 返回一个由操作符右边的key path所指定的对象属性组成的数组。其中@distinctUnionOfObjects会对数组去重, 而@unionOfObjects不会。

[inventoryvalueForKeyPath:@"@unionOfObjects.name"];//"iPhone 5","iPhone 5","iPhone 5","iPad Mini","MacBook Pro","MacBook Pro"[inventoryvalueForKeyPath:@"@distinctUnionOfObjects.name"];//"iPhone 5","iPad Mini","MacBook Pro"

数组和集合操作符

数组和集合操作符跟对象操作符很相似,只不过它是在NSArray和NSSet所组成的集合中工作的。

@distinctUnionOfArrays/@unionOfArrays: 返回了一个数组,其中包含这个集合中每个数组对于这个操作符右面指定的key path进行操作之后的值。正如你期望的,distinct版本会移除重复的值。

@distinctUnionOfSets:和@distinctUnionOfArrays差不多, 但是它期望的是一个包含着NSSet对象的NSSet,并且会返回一个NSSet对象。因为集合不能包含重复的值,所以它只有distinct操作。

你可能感兴趣的:(KVC底层原理)