KVC(Key-value coding)键值编码,单看这个名字可能不太好理解。其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。
KVC在iOS中的定义
无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-C中有个显式的NSKeyValueCoding类别名,而Swift没有,也不需要)。所以对于所有继承了NSObject的类型,也就是几乎所有的Objective-C对象都能使用KVC(一些纯Swift类和结构体是不支持KVC的),下面是KVC最为重要的四个方法


//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_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成员变量、实例变量、属性区别?

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


画的流程图

代码示例:



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




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




set取值的操作,按照取值步骤3,最终输出结果 如图set4.png
KVC的赋值过程
赋值过程的官方文档截图









变量setter的流程

上面流程测试







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


代码测试













NSMutableSet的set过程

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

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



自定义KVC逻辑
set值简单流程

get值简单流程

利用KVC处理异常
1.找不到key,setvalue:forkey 没这个key

2.传值为nil


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

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

KVC进阶用法
keyPath中的集合运算符的使用
①简单集合运算符
简单集合运算符共有
@avg 首先把集合中的每个对象都转换为double类型,然后计算其平均值,最后返回一个值为该平均值的NSNumber对象
@count 返回一个值为集合中对象总数的NSNumber对象
@max 使用compare:方法来确定最大值。所以为了让其正常工作,集合中所有的对象都必须支持和另一个对象的比较
@min 和@max一样,但是返回的是集合中的最小值。
@sum 首先把集合中的每个对象都转换为double类型,然后计算其总,最后返回一个值为这个总和的NSNumber对象


对象操作符
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操作。
网友评论