一、KVC是什么
二、怎么使用KVC
三、KVC的底层实现
四、KVC常见面试题
一、KVC是什么
KVC全称Key-Value Coding,翻译过来是键值编码,是一种通过一个key
来访问某个属性的机制。
解释一下,我们通常不都是通过setter
、getter
方法来访问公开属性的嘛,私有属性更是访问不到,而KVC则是提供了另外一种访问属性的方式——即通过一个key
来访问某个属性,无论是公开属性还是私有属性。
二、怎么使用KVC
KVC最常用的API如下,更多的API可以自己去查。
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (id)valueForKeyPath:(NSString *)keyPath;
setValue:forKey:
和valueForKey:
只能用来访问当前对象的属性,而setValue:forKeyPath:
和valueForKeyPath:
还可以用来访问当前对象的属性的属性,并可以这样一层一层套下去,也就是说KeyPath
比Key
这种方式要强大一些。
举个简单例子:
// INEPerson.h
#import <Foundation/Foundation.h>
// Cat类
@interface INECat : NSObject
@property (nonatomic, assign) NSInteger weight;
@end
@interface INEPerson : NSObject
// 公开属性
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) INECat *cat;
@end
// INEPerson.m
@interface INEPerson ()
// 私有属性
@property (nonatomic, assign) NSInteger height;
@end
// ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
INEPerson *person = [[INEPerson alloc] init];
// 通常是通过setter、getter方法来访问公开属性的
person.age = 25;
NSLog(@"%ld", person.age);// 25
// 无法访问私有属性
// person.height = 177;
// NSLog(@"%ld", person.age);
// 通过KVC来访问公开属性
[person setValue:@26 forKey:@"age"];
NSLog(@"%ld", [[person valueForKey:@"age"] integerValue]);// 26
person.cat = [[INECat alloc] init];
[person setValue:@11 forKeyPath:@"cat.weight"];
NSLog(@"%ld", [[person valueForKeyPath:@"cat.weight"] integerValue]);// 11
// 通过KVC来访问私有属性
[person setValue:@177 forKey:@"height"];
NSLog(@"%ld", [[person valueForKey:@"height"] integerValue]);// 177
}
三、KVC的底层实现
KeyPath
和Key
是同理的,下面以Key
方式为例。
1、KVC的赋值流程
setValue:forKey:
方法内部:
- 第一波,查找
setter
方法
首先会把参数key
的第一个字母变成大写的。
然后根据这个Key
按顺序去查找对应的setter
方法,包括setKey:
和_setKey:
,如果找到了对应的setter
方法,就调用并把参数value
传进去,从而修改掉属性的值(本质上还是修改掉了成员变量的值)。
- 第二波,直接给成员变量赋值
如果没有找到对应的setter
方法,就会去查看accessInstanceVariablesDirectly
方法的返回值(能否直接访问成员变量,默认返回YES
),如果不能直接访问成员变量,就会报一个“找不到key
”的错。
如果能直接访问成员变量,就会按顺序去查找对应的成员变量,_key
、_isKey
、key
、isKey
,如果找到了对应的成员变量,就直接给成员变量赋值(若设置了KVO的话,此时还会触发KVO,因为KVC在此处也调用了willChangeValueForKey:
和didChangeValueForKey:
方法),如果没有找到对应的成员变量,就会报一个“找不到key
”的错。
2、KVC的取值流程
valueForKey:
方法内部:
- 第一波,查找
getter
方法
首先会把参数key
的第一个字母变成大写的。
然后根据这个Key
和key
按顺序去查找对应的setter
方法,包括getKey
、key:
、isKey:
、_key:
,如果找到了对应的getter
方法,就调用并返回属性的值(本质上还是读取成员变量的值)。
- 第二波,直接读取成员变量的值
如果没有找到对应的getter
方法,就会去查看accessInstanceVariablesDirectly
方法的返回值(能否直接访问成员变量,默认返回YES
),如果不能直接访问成员变量,就会报一个“找不到key
”的错。
如果能直接访问成员变量,就会按顺序去查找对应的成员变量,包括_key
、_isKey
、key
、isKey
,如果找到了对应的成员变量,就直接读取成员变量的值,如果没有找到对应的成员变量,就会报一个“找不到key
”的错。
四、KVO常见面试题
1、通过KVC修改属性的话,会不会触发KVO?
答案:
会。
因为KVC的底层实现就是首先调用属性的setter
方法设置,当然会触发KVO。
即便你不是修改属性,而是通过KVC直接修改成员变量——即类里面没有对应的setter
方法,KVC也会触发KVO,因为KVC底层在修改成员变量的时候也调用了willChangeValueForKey:
和didChangeValueForKey:
方法。
[self.person1 willChangeValueForKey:@"age"];
self.person1->_age = 26;
[self.person1 didChangeValueForKey:@"age"];
网友评论