KVC(Key-value coding)键值编码,是一个基于NSKeyValueCoding非正式协议实现的机制,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值,而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性,而不是在编译时确定。KVC是基于OC的动态特性和Runtime机制的。
NSObject(NSKeyValueCoding)
- (nullableid)valueForKey:(NSString*)key;//直接通过Key来取值
- (void)setValue:(nullableid)value forKey:(NSString*)key;//通过Key来设值
- (nullableid)valueForKeyPath:(NSString*)keyPath;//通过KeyPath来取值
- (void)setValue:(nullableid)value forKeyPath:(NSString*)keyPath;//通过KeyPath来设值
KVC的定义都是对NSObject的扩展来实现的,OC中有个显式的NSKeyValueCoding类别名,所以对于所有继承了NSObject的类型,都能使用。
实现了访问器方法的类中,使用点语法和KVC访问对象其实差别不大,二者可以任意混用。但是没有实现访问器方法的类中,点语法无法使用,这时KVC就有优势了。KVC 不但能够赋值,而且还能破坏只读的特性。OC中的KVC操作很暴力,这样做就会破坏类的封装性,类的私有属性和只读属性都可以通过KVC去访问。
KVC支持嵌套。因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.语法把一个key链接起来,这样就可以根据这个路径访问下去(KVC在按照键值路径取值时,会自动层层深入,获取对应的键值)。
a.定义一个类
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
b.引用这个类
@interface OC_SWIFTViewController ()
@property (strong, nonatomic) Person *person;
@end
c.设置和获取
- (void)viewDidLoad {
[super viewDidLoad];
self.person= [[Person alloc] init];
[self setValue:@"Jack" forKeyPath:@"person.name"];
NSString*name = [self valueForKeyPath:@"person.name"];
NSLog(@"%@",name);
}
d.输出结果
2019-11-27 17:46:15.643288+0800 OC-Swift[6346:274818] Jack
如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set属性名时,会直接用setValue:forUndefinedKey:方法。valueForUndefinedKey:错误拦截,当key的值是没有定义的,这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来。
KVC要设值,那么就要找到对象中对应的key,KVC在内部是按什么样的顺序来寻找key的:
1.首先搜索 setter 方法,有就直接赋值。
2.如果上面的 setter 方法没有找到,再检查类方法+ (BOOL)accessInstanceVariablesDirectly
i.返回 NO,则执行setValue:forUNdefinedKey:
ii.返回 YES,则按_<key>,_<isKey>,<key>,<isKey>的顺序搜索成员名。
3.还没有找到的话,就调用setValue:forUndefinedKey: