• KVC Key-value coding 键值编码。它是一种可以通过字符串的名字(key)来访问类属性的机制。
  • KVC支持类对象和内建基本数据类型。
  • KVC是KVO、Core Data、CocoaBindings的技术基础,他们都是利用了OC的动态性。

常见的API

  1. - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
  2. - (void)setValue:(id)value forKey:(NSString *)key;
  3. - (id)valueForKeyPath:(NSString *)keyPath;
  4. - (id)valueForKey:(NSString *)key;

内部原理

setValue:forKey:的原理

image.png

  • accessInstanceVariablesDirectly方法的默认返回值是YES

valueForKey:的原理

image.png

使用场景

KVC在iOS开发中是绝不可少的利器,这种基于运行时的编程方式极大地提高了灵活性,简化了代码,甚至实现很多难以想像的功能,KVC也是许多iOS开发黑魔法的基础。下面我来列举iOS开发中KVC的使用场景

  • 动态地取值和设值
  • 访问和修改私有变量
  • Model和字典转换
  • 修改一些控件的内部属性
    • 在xib/Storyboard中,也可以使用KVC,下面是在xib中使用KVC把图片边框设置成圆角
    • image.png
  • 用KVC中的函数操作集合

KVC默认的实现方法有NSOject提供,这种方法及支持对象也支持简单数据类型。

访问变量的几种方式:

  1. public型,通过 -> 直接访问:
  2. 利用属性访问
  3. 利用KVC,即使该属性是private也可以访问

KVC路径访问

  1. [book valueForKeyPath:@"authorObj.name"];
  2. author *authorObj=[[author alloc] init];
  3. [authorObj setValue:@"niudun" forKey:@"name"];
  4. [book setValue:authorObj forKey:@"authorObj"];
  5. NSLog(@"the author of book is%@",[book valueForKeyPath:@"authorObj.name"]);

操作集合

Apple对KVC的valueForKey:方法作了一些特殊的实现,比如说NSArray和NSSet这样的容器类就实现了这些方法。所以可以用KVC很方便地操作集合

用KVC实现高阶消息传递

当对容器类使用KVC时,valueForKey:将会被传递给容器中的每一个对象,而不是容器本身进行操作。结果会被添加进返回的容器中,这样,开发者可以很方便的操作集合来返回另一个集合。

  1. NSArray* arrStr = @[@"english",@"franch",@"chinese"];
  2. NSArray* arrCapStr = [arrStr valueForKey:@"capitalizedString"];
  3. for (NSString* str in arrCapStr) {
  4. NSLog(@"%@",str);
  5. }
  6. NSArray* arrCapStrLength = [arrStr valueForKeyPath:@"capitalizedString.length"];
  7. for (NSNumber* length in arrCapStrLength) {
  8. NSLog(@"%ld",(long)length.integerValue);
  9. }

用KVC中的函数操作集合

  1. //属性相加
  2. NSString *sum= [persons valueForKeyPath:@"Person.@sum.age"];
  3. NSLog(@"sum = %@",sum);
  4. //数量
  5. NSString *count= [persons valueForKeyPath:@"Person.@count.age"];
  6. NSLog(@"count = %@",count);
  7. //最大值
  8. NSString *max= [persons valueForKeyPath:@"Person.@max.age"];
  9. NSLog(@"max = %@",max);
  10. //最小值
  11. NSString *min= [persons valueForKeyPath:@"Person.@min.age"];
  12. NSLog(@"min = %@",min);
  13. //平均值
  14. NSString *avg= [persons valueForKeyPath:@"Person.@avg.age"];
  15. NSLog(@"avg = %@",avg);
  • 对象运算符
    比集合运算符稍微复杂,能以数组的方式返回指定的内容,一共有两种:
  1. @distinctUnionOfObjects
  2. @unionOfObjects

它们的返回值都是NSArray,区别是前者返回的元素都是唯一的,是去重以后的结果;后者返回的元素是全集。
用法如下:

  1. NSLog(@"distinctUnionOfObjects");
  2. NSArray* arrDistinct = [arrBooks valueForKeyPath:@"@distinctUnionOfObjects.price"];
  3. for (NSNumber *price in arrDistinct) {
  4. NSLog(@"%f",price.floatValue);
  5. }
  6. NSLog(@"unionOfObjects");
  7. NSArray* arrUnion = [arrBooks valueForKeyPath:@"@unionOfObjects.price"];
  8. for (NSNumber *price in arrUnion) {
  9. NSLog(@"%f",price.floatValue);
  10. }
  • Array和Set操作符
  1. - `@distinctUnionOfArrays`:该操作会返回一个数组,这个数组包含不同的对象,不同的对象是在从关键路径到操作器右边的被指定的属性里
  2. - `@unionOfArrays` 该操作会返回一个数组,这个数组包含的对象是在从关键路径到操作器右边的被指定的属性里和@distinctUnionOfArrays不一样,重复的对象不会被移除
  3. - `@distinctUnionOfSets` `@distinctUnionOfArrays`类似。因为Set本身就不支持重复。

面试题

1. 通过KVC修改属性会触发KVO么?

  • 会触发KVO ,内部手动触发的KVO