文章目录
前言
KVC
的全称是Key-Value Coding
,翻译成中文叫做键值编码
KVC提供了一种间接访问属性方法或成员变量 的机制,允许通过字符串来访问对应的属性方法或成员变量
它是一个非正式的Protocol,提供一种机制来间接访问对象的属性,而不是通过调用Setter、Getter方法访问。KVO
就是基于 KVC
实现的关键技术之一。
一、KVC常用方法
通过key
设值/取值
bash
//直接通过Key来取值
- (nullable id)valueForKey:(NSString *)key;
//通过Key来设值
- (void)setValue:(nullable id)value forKey:(NSString *)key;
通过keyPath
(即路由
)设值/取值
bash
//通过KeyPath来取值
- (nullable id)valueForKeyPath:(NSString *)keyPath;
//通过KeyPath来设值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
其他方法
bash
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
+ (BOOL)accessInstanceVariablesDirectly;
//KVC提供属性值正确性验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。
- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//如果Key不存在,且KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。
- (nullable id)valueForUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法
- (void)setNilValueForKey:(NSString *)key;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
二、key与keypath
区别
key
:只能接受当前类所具有的属性,不管是自己的,还是从父类继承过来的
keypath
:除了能接受当前类的属性,还能接受当前类属性的属性,即可以接受关系链,然后进行深层访问
key用法
bash
People *t1 = [[People alloc] init];
Address *t2 = [[Address alloc] init];
t1.Ad = t2;
[t1 setValue:@"顶针" forKey:@"name"];
NSLog(@"%@", [t1 valueForKey:@"name"]);
输出:
keypath用法
输出:
三、批量存值操作
同样,也可以通过KVC进行批量操作,使用对象调用setValuesForKeysWithDictionary
:方法时,可以传入一个包好key
、value
的字典进去,KVC
可以将所有数据按照属性名和字典的key进行匹配,并将value
给对象的属性赋值
bash
NSDictionary *dictionarySecond = @{@"name":@"顶针", @"age":@"11", @"sex":@"女"};
People *t3 = [[People alloc] init];
[t3 setValuesForKeysWithDictionary:dictionarySecond];
NSLog(@"name = %@, age = %ld, sex = %@",t3.name, (long)t3.age, t3.sex);
四、字典与模型相互转化
如果model
属性和dic
不匹配,可以重写方法-(void)setValue:(id)value forUndefinedKey:(NSString *)key。
这一点后面会讲到
字典转模型
bash
NSDictionary *dictionary = @{@"name":@"stu1", @"age":@66, @"sex":@"nv"};
StudentModel *model = [[StudentModel alloc] init];
[model setValuesForKeysWithDictionary:dictionary];
NSLog(@"model.name:%@",model.name);
NSLog(@"model.age:%@",model.age);
NSLog(@"model.sex:%@",model.studentSex);
输出:
模型转字典
bash
NSDictionary *tempModelDictionary = [model dictionaryWithValuesForKeys:@[@"name", @"age", @"studentSex"]];
NSLog(@"tempModelDictionary : %@", tempModelDictionary);
输出:
五、KVC底层原理
KVC设值底层原理
在日常开发中,针对对象属性的赋值,一般有以下两种方式
- 直接通过
setter
方法赋值 - 通过KVC键值编码的相关API赋值
bash
LGPerson *person = [[LGPerson alloc] init];
// 1、一般setter 方法
person.name = @"CJL_哈哈";
// 2、KVC方式
[person setValue:@"CJL_嘻嘻" forKey:@"name"];
下面针对setValue:forKey
进行底层原理探索
在这里,我们通过Key-Value Coding Programming Guide苹果官方文档来研究,针对设值流程,有如下说明
当调用setValue:forKey:
设置属性value
时,其底层的执行流程为
- 【第一步】首先查找是否有这三种setter方法,按照查找顺序为
set<Key>:-> _set<Key> -> setIs<Key>
如果有其中任意一个setter
方法,则直接设置属性的value
(主注意:key
是指成员变量名,首字符大小写需要符合KVC
的命名规范)
如果都没有则进入第二步
- 【第二步】:如果没有第一步中的三个简单的
setter
方法,则查找accessInstanceVariablesDirectly
是否返回YES
如果返回YES
,则查找间接访问的实例变量进行赋值,查找顺序为:_<key> -> _is<Key> -> <key> -> is<Key>
如果找到其中任意一个实例变量,则赋值,如果都没有,则进入【第三步】
- 【第三步】如果
setter
方法 或者 实例变量都没有找到,系统会执行该对象的setValue:forUndefinedKey
:方法,默认抛出NSUndefinedKeyException
类型的异常
综上所述,KVC
通过 setValue:forKey:
方法设值的流程以设置LGPerson
的对象person
的属性name
为例,如下图所示
KVC取值底层原理
当调用valueForKey:
时,其底层的执行流程如下
- 首先会按照
getKey、key、isKey、_key
的顺序查找方法,找到直接调用取值 - 若未找到,则查看
+ (BOOL)accessInstanceVariablesDirectly
的返回值,若返回NO,则直接抛出NSUnknowKeyExpection
异常; - 若返回的
YES
,则按照_key、_isKey、key、isKey
的顺序查找成员变量,找到则取值; - 找不到则调用
valueForUndefinedKey
:抛出NSUnknowKeyExpection
异常;