KVC原理总结

iOS 的KVC技术比较常用,可在运行时动态地对一个对象的属性赋值,并且如果该key是有添加KVO监听, 也会触发这个监听。下面是KVC的实现原理总结。

一、KVC的设值原理
kvc设值原理.png

根据上图可以很清晰的知道KVC的设值过程2+4(即2个方法4个成员变量)。假设要设值的属性名为key
1> 当调用setValue:forKey:或者setValue:forKeypath:方法时,是首先查找对象方法 setKey:,如果有则直接调用,就此完成了KVC; 如果没有这个方法则查找_ setKey:方法调用,完成KVC。

2> 当上述2个方法都未找到,则调用对象的类方法 accessInstanceVariablesDirectly的返回值,如果为NO, 表示不允许访问成员变量,则抛出异常如上图; 如果为YES, 则查找是否存在可以设置的成员变量,按照_key_isKeykeyisKey顺序查找,找到了一个就不用继续往后查找了,直接对该成员变量赋值完成KVC,如果没有找到,则抛出如图异常。

二、KVC的取值原理

KVC的取值原理.png

KVC的取值过程如图4+4(即4个方法+4个成员变量),假设要取值的属性为key
1> 当调用valueForkey:方法时,首先按照顺序getKeykeyisKey_key查找对象方法,如果找到了方法,则调用方法拿到返回值即完成KVC取值.

2> 如果上述方法都没有找到,则调用对象的类方法 accessInstanceVariablesDirectly的返回值,如果为NO, 表示不允许访问成员变量,则抛出异常如上图; 如果为YES, 则查找是否存在可以取值的成员变量,按照_key_isKeykeyisKey顺序查找,找到了一个就不用继续往后查找了,直接取该成员变量完成KVC取值,如果没有找到,则抛出如图异常。

三、KVC触发KVO
1. 如果有setKey:或者_setKey:方法

按照KVO原理有setKey:是会触发KVO通知的;下面是测试没有setKey:方法但有有_setKey: (在没有对应属性只有方法的情况下测试的)

@implementation Person{
    NSString *_height;
}
+ (BOOL)accessInstanceVariablesDirectly{
    return NO;
}
- (void)_setKey:(NSString *)key{
    NSLog(@"%s", __func__);
}
- (NSString *)_key{
    NSLog(@"%s", __func__);
    return @"234";
}
@end

// 在控制器中测试代码
- (void)testKVCForKVO{
    // 1. 无setKey,有_setKey
    Person *person = [[Person alloc] init];
    [person addObserver:self forKeyPath:@"key" options:NSKeyValueObservingOptionNew context:nil];
    
    [person setValue:@"111" forKey:@"key"];
    [person removeObserver:self forKeyPath:@"key"];// person在dealloc时未移除观察者会奔溃
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"observeValueForKeyPath: %@", change);
}

--- 结果是有setKey:或者_setKey方法就会触发KVO,原因是内部调用了KVO通知的函数_NSSetObjectValueAndNotify

KVC调用setter触发KVO.png

2.如果没有KVC设置值的2个setter方法,单独对成员变量使用KVC会触发KVO吗?

如上代码中,对_height成员使用KVC设置值, Person类中方法+ (BOOL)accessInstanceVariablesDirectly返回值设置为YES;
测试代码如下

- (void)testKVCForKVO{
    // 1. 无setKey,有_setKey
    Person *person = [[Person alloc] init];
    [person addObserver:self forKeyPath:@"_height" options:NSKeyValueObservingOptionNew context:nil];
    
    [person setValue:@(180) forKey:@"_height"];
    
    [person removeObserver:self forKeyPath:@"_height"];// person在dealloc时未移除观察者会奔溃
}

---结果也是会触发,原因则是内部会调用didChangeValueForKey:
手动触发KVO时的两个方法中的后一个

- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;
KVC设置成员变量触发KVO.png

以上两种情况都会触发KVO。
以上完结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • KVC是Key Value Coding的简称。它是一种可以通过字符串的名字(key)来访问类属性的机制。而不是通...
    153037c65b0c阅读 11,559评论 15 17
  • 简介 KVC(Key-value coding)键值编码,翻译一下就是指iOS的开发中,可以允许开发者通过Key名...
    6ffd6634d577阅读 1,346评论 1 9
  • KVC简单介绍 KVC(Key-value coding)键值编码,就是指iOS的开发中,可以允许开发者通过Key...
    公子无礼阅读 1,438评论 0 6
  • 不管是平常开发还是找工作面试中,KVC、KVO的原理都是面试官比较喜欢问的问题。最近抽时间研究了一下KVC和KVO...
    coolLee阅读 1,245评论 0 2
  • 《古都夜景》 (一) 夏夜湖边酒色楼, 歌舞声声情伴愁。 千年帝都不夜天, 达官显贵争艳酬。 秦岭深处是商州, 青...
    常乐人生阅读 855评论 4 4