KVC

来源:互联网 发布:vs2017 golang 编辑:程序博客网 时间:2024/04/28 05:00

一、概述

KVC(Key-value coding)键值编码,iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性,而不是在编译时确定。

在实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用。但是没有访问起方法的类中,点语法无法使用,这时KVC就有优势了。

 

二、KVC的定义与使用

1、KVC相关的方法

KVC的定义都是对NSObject的扩展来实现的,Objective-c中有个显式的NSKeyValueCoding类别名,所以对于所有继承了NSObject在类型,都能使用KVC,下面是KVC最为重要的四个方法

当然NSKeyValueCoding类别中还有其他的一些方法,例如:

 

同时苹果对一些容器类比如NSArray或者NSSet等,KVC有着特殊的实现。

有序集合对应方法如下:

 

无序集合对应方法如下:

 

2、一对多关系(To-Many)中的集合访问器方法

我们平时大部分使用的属性都是一对一关系(To-One),比如Person类中的name属性,每个人只有一个名字。但也有一对多的关系,比如Person中有一个friendsName属性,这是个集合(在Objective-C中可以是NSArray,NSSet等),保存的是一个人的所有朋友的名字。

当操作一对多的属性中的内容时,我们有两种选择:

(1)间接操作
先通过KVC方法取到集合属性,然后通过集合属性操作集合中的元素。实际开发中最常用的方法。

 

(2)直接操作
苹果为我们提供了一些方法模板(即上面提到的有序集合和无序集合对应的方法),我们可以以规定的格式实现这些方法来达到访问集合属性中元素的目的。不过在实际开发中一般不使用直接操作的方法,使用间接操作就足够了,苹果甚至都没有让这些方法以哪怕是非正式协议的形式出现,而只是在编程指南中提了一下。

 

 

3、KVC对数值和结构体型属性的支持

KVC可以自动的将数值或结构体型的数据打包或解包成NSNumber或NSValue对象,以达到适配的目的。

举个例子,Person类有个NSInteger类型的age属性,如下:

 

(1)修改值

我们通过KVC技术使用如下方式设置age属性的值:

我们赋给age的是一个NSNumber对象,KVC会自动的将NSNumber对象转换成NSInteger对象,然后再调用相应的访问器方法设置age的值。

 

(2)获取值

同样,以如下方式获取age属性值:

这时,会以NSNumber的形式返回age的值。

 

例如:

输出结果:

 

需要注意的是我们不能直接将一个数值通过KVC赋值的,我们需要把数据转为NSNumber和NSValue类型传入,那到底哪些类型数据要用NSNumber封装哪些类型数据要用NSValue封装呢?看下面这些方法的参数类型就知道了:

可以使用NSNumber的数据类型有:

就是一些常见的数值型数据。

 

可以使用NSValue的数据类型有:

NSValue主要用于处理结构体型的数据,它本身提供了如上集中结构的支持。任何结构体都是可以转化成NSValue对象的,包括其它自定义的结构体。

 

三、KVC中使用KeyPath

在开发过程中,一个类的成员变量有可能是其他的自定义类,你可以先用KVC获取出来该属性,然后再次用KVC来获取这个自定义类的属性,但这样是比较繁琐的,对此,KVC提供了一个解决方案,那就是键路径KeyPath。

KVC 同样允许我们通过关系来访问对象。假设 people 对象有属性 addressaddress 有属性 country,我们可以这样通过people 来访问country:

值得注意的是这里我们调用 -valueForKeyPath: 而不是 -valueForKey:

例如:

打印结果:

 

KVC键值验证(Key-Value Validation)

KVC提供了验证Key对应的Value是否可用的方法:

该方法默认的实现是调用一个如下格式的方法:

例如:

打印结果:

这样就给了我们一次纠错的机会。需要指出的是,KVC是不会自动调用键值验证方法的,就是说我们如果想要键值验证则需要手动验证。但是有些技术,比如CoreData会自动调用。

上面的代码简单在展示了KeyPath是怎么用的。如果你不小心错误的使用了key而非KeyPath的话,KVC会直接查找address.country这个属性,很明显,这个属性并不存在,所以会再调用UndefinedKey相关方法。而KVC对于KeyPath是搜索机制第一步就是分离key,用小数点.来分割key,然后再像普通key一样按照先前介绍的顺序搜索下去。接下来就来研究KVC是怎么寻找Key的。

 

四、用KVC中的函数操作集合

KVC同时还提供了很复杂的函数,主要有下面这些:
1、简单集合运算符
简单集合运算符共有@avg, @count , @max , @min ,@sum5种,都表示什么直接看下面例子就明白了, 目前还不支持自定义。

运行结果:

 

2、对象运算符
比集合运算符稍微复杂,能以数组的方式返回指定的内容,一共有两种:
@distinctUnionOfObjects
@unionOfObjects
它们的返回值都是NSArray,区别是前者返回的元素都是唯一的,是去重以后的结果;后者返回的元素是全集。

例如:

运行结果:

 

五、setValue:forKey:方法赋值的原理

例如对于:[item setValue:@”value” forKey:@”property”],具体实现为:

  1. 首先去模型中查找有没有setProperty,找到,直接调用赋值 [self setProperty:@”value”]
  2. 去模型中查找有没有property属性,有,直接访问属性赋值  property = value
  3. 去模型中查找有没有_property属性,有,直接访问属性赋值 _property = value
  4. 找不到,就会直接报错 setValue:forUndefinedKey:报找不到的错误

如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUNdefinedKey:方法。

 

所以,我们使用KVC要有以下三个条件:

  1. 必须保证模型中定义的属性要大于或等于字典中key的数量。
  2. 模型中的基本数据类型无法进行转换。
  3. 属性的名字必须和键相同,否则找不到相关属性会报错。

 

六、KVC异常处理

KVC中最常见的异常就是不小心使用了错误的Key,或者在设值中不小心传递了nil的值,KVC中有专门的方法来处理这些异常。
通常在用KVC操作Model时,抛出异常的那两个方法是需要重写的。虽然一般很小出现传递了错误的Key值这种情况,但是如果不小心出现了,直接抛出异常让APP崩溃显然是不合理的。一般在这里直接让这个Key打印出来即可,或者有些特殊情况需要特殊处理。
通常情况下,KVC不允许你要在调用setValue:属性值 forKey:@”name”(或者keyPath)时对非对象传递一个nil的值。很简单,因为值类型是不能为nil的。如果你不小心传了,KVC会调用setNilValueForKey:方法。这个方法默认是抛出异常,所以一般而言最好还是重写这个方法。

如果重写setNilValueForKey:就没问题了:

 

七、KVC和字典

当对NSDictionary对象使用KVC时,valueForKey:的表现行为和objectForKey:一样。所以使用valueForKeyPath:用来访问多层嵌套的字典是比较方便的。

KVC里面还有两个关于NSDictionary的方法:

dictionaryWithValuesForKeys:是指输入一组key,返回这组key对应的属性,再组成一个字典。
setValuesForKeysWithDictionary是用来修改Model中对应key的属性。下面直接用代码会更直观一点:

打印结果:

打印出来的结果完全符合预期。

0 0
原创粉丝点击