KVC 含义
我们看下官方给的定义:
Key-value coding is a mechanism enabled by the NSKeyValueCoding informal protocol that objects adopt to provide indirect access to their properties. 【译】KVC 是通过 NSKeyValueCoding 这个非正式协议启用的一种机制,而遵循了这个协议的对象就提供了对其属性的间接访问。
KVC 的用途

1.基本用法
直接对属性或者成员变量进行取值和赋值
LGPerson *person = [[LGPerson alloc] init];
[person setValue:@"KC" forKey:@"name"];
[person setValue:@19 forKey:@"age"];
[person setValue:@"酷C" forKey:@"myName"];
NSLog(@"%@ - %@ - %@",[person valueForKey:@"name"],[person valueForKey:@"age"],[person valueForKey:@"myName"]);
2.操作集合类型
针对集合属性,可以直接通过mutableArrayValueForKey对对象的集合属性进行操作更改
person.array = @[@"1",@"2",@"3"];
// KVC 的方式
NSMutableArray *ma = [person mutableArrayValueForKey:@"array"];
ma[0] = @"100";
NSLog(@"%@",[person valueForKey:@"array"]);
3.访问非对象属性
针对结构体,KVC也可以直接操作,但是操作时候需要将结构体转成NSValue类型
typedef struct {
float x, y, z;
} ThreeFloats;
ThreeFloats floats = {1., 2., 3.};
NSValue *value = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
[person setValue:value forKey:@"threeFloats"];
NSValue *reslut = [person valueForKey:@"threeFloats"];
NSLog(@"%@",reslut);
ThreeFloats th;
[reslut getValue:&th] ;
NSLog(@"%f - %f - %f",th.x,th.y,th.z);
4.层层访问
假如对象的属性也是对象,那么KVC可以通过keyPath来操作对象属性的属性
LGStudent *student = [[LGStudent alloc] init];
student.subject = @"iOS";
person.student = student;
[person setValue:@"大师班" forKeyPath:@"student.subject"];
NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);
5.集合操作符
5.1 字典操作
假如字典的key和一个对象的属性都一样,那么可以通过setValuesForKeysWithDictionary直接将字典的value赋值给对象相应的属性,同样,也可以通过dictionaryWithValuesForKeys将对象转换成字典
- (void)dictionaryTest{
NSDictionary* dict = @{
@"name":@"Cooci",
@"nick":@"KC",
@"subject":@"iOS",
@"age":@18,
@"length":@180
};
LGStudent *p = [[LGStudent alloc] init];
// 字典转模型
[p setValuesForKeysWithDictionary:dict];
NSLog(@"%@:%@",p,p.name);
// 键数组转模型到字典
NSArray *array = @[@"name",@"age"];
NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
NSLog(@"%@",dic);
}
5.2 操作数组元素的信息(KVC消息传递)
通过api可以拿到数组元素的长度,也可以对数组元素进行操作得到新的数组
NSArray *array = @[@"Hank",@"Cooci",@"Kody",@"CC"];
//得到数组中所有元素的长度
NSArray *lenStr= [array valueForKeyPath:@"length"];
NSLog(@"%@",lenStr);
//将数组中所有值全变成小写
NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
NSLog(@"%@",lowStr);
//将数组中所有值全变成大写
NSArray *uppercaseStr= [array valueForKeyPath:@"uppercaseString"];
NSLog(@"%@",uppercaseStr);
5.3 聚合操作符
@avg:取平均值 @count:取个数 @max:取最大值 @min:取最小值 @sum:求和
- (void)aggregationOperator{
NSMutableArray *personArray = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
LGStudent *p = [LGStudent new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"length":@(175 + 2*arc4random_uniform(6)),
};
[p setValuesForKeysWithDictionary:dict];
[personArray addObject:p];
}
NSLog(@"%@", [personArray valueForKey:@"length"]);
/// 平均身高
float avg = [[personArray valueForKeyPath:@"@avg.length"] floatValue];
NSLog(@"%f", avg);
int count = [[personArray valueForKeyPath:@"@count.length"] intValue];
NSLog(@"%d", count);
int sum = [[personArray valueForKeyPath:@"@sum.length"] intValue];
NSLog(@"%d", sum);
int max = [[personArray valueForKeyPath:@"@max.length"] intValue];
NSLog(@"%d", max);
int min = [[personArray valueForKeyPath:@"@min.length"] intValue];
NSLog(@"%d", min);
}
KVC 的底层实现
KVC-赋值过程

根据上面官方文档得知:
-
- 先依次查询有没有相关的方法:set< Key>:、_set< Key>:、setIs< Key>: 找到直接进行调用赋值。
-
- 若没有相关方法时,会查看类方法accessInstanceVariablesDirectly是否为YES时进入下一步。否则进入步骤4
-
- 为YES时,可以直接访问成员变量的来进行赋值,依次寻找变量 _< key >、 _is< Key>、 < key>、 is< Key>。找到则直接赋值,否则进入下一步。
- 将会调用**setValue:forUndefinedKey:**方法进行抛出异常。可以自定义的实现为未找到的场景来避免抛出异常的行为。
KVC-取值过程
-
- 先依次寻找是否有相关成员变量:get< Key>,< key>,is< Key>,或者 _< key >, 有则进入步骤4,否则下一步。
-
- 查看类方法accessInstanceVariablesDirectly是否为YES,是则进入下一步,否则进入步骤5
-
- 依次寻找 _< key>,_is< Key>,< key>,或者 is< Key> 成员变量是否有值,有则进入步骤4,否则进入步骤5
-
- 如果检索到的属性值是对象指针,则只返回结果。如果值是支持的标量类型NSNumber,则将其存储在NSNumber实例中并 返回该值。如果结果是NSNumber不支持的标量类型,则转换为NSValue对象并返回该对象。
-
- 将会调用 setValue:forUndefinedKey: 方法进行抛出异常。可以自定义的实现为未找到的场景来避免抛出异常的行为
总结
在开发中经常用用到kvc,需要注意的是,ios13以后,对部分私有属性,使用kvc的话,会崩溃,这点要注意。