探索iOS底层原理第三篇——KVC
2019-05-11 本文已影响4人
经天纬地
本系列是学习iOS底层原理过程中的记录笔记第三篇,往期目录:
探索iOS底层原理开篇——对象本质
探索iOS底层原理第二篇——KVO
国际惯例先抛出面试题:
- 通过KVC修改属性会触发KVO么?
- KVC的赋值和取值过程是怎样的?原理是什么?
分析过程:
KVC的全称是Key-Value Coding
,俗称“键值编码”,可以通过一个key来访问某个属性
常见的API有
(void)setValue:(id)value forKeyPath:(NSString *)keyPath;
(void)setValue:(id)value forKey:(NSString *)key;
(id)valueForKeyPath:(NSString *)keyPath;
(id)valueForKey:(NSString *)key;
新建一个命令行项目,新建Person类作为触发kvc,新建Observe类作为KVO监听器:
main.m:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Observer *observer = [[Observer alloc] init];
Person *person = [[Person alloc] init];
// 添加KVO监听
[person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 通过KVC修改age属性
[person setValue:@10 forKey:@"age"];
// setAge:
// 移除KVO监听
[person removeObserver:observer forKeyPath:@"age"];
}
return 0;
}
Observer.m:
@implementation Observer
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"observeValueForKeyPath - %@", change);
}
@end
结果确实有打印出KVO方法:

从上述结果看通过KVC修改属性值确实能调用KVO属性监听.上一篇我们讲到KVO实际上会调用willChangeValueForKey
和didChangeValueForKey
方法,我们再验证一下是否通过kvc修改属性值会触发这两个方法:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Observer *observer = [[Observer alloc] init];
Person *person = [[Person alloc] init];
// 添加KVO监听
[person addObserver:observer forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
// 通过KVC修改age属性
[person setValue:@10 forKey:@"age"];
// 移除KVO监听
[person removeObserver:observer forKeyPath:@"age"];
}
return 0;
}
在Person.m中添加代码:
- (void)willChangeValueForKey:(NSString *)key
{
[super willChangeValueForKey:key];
NSLog(@"willChangeValueForKey - %@", key);
}
- (void)didChangeValueForKey:(NSString *)key
{
NSLog(@"didChangeValueForKey - begin - %@", key);
[super didChangeValueForKey:key];
NSLog(@"didChangeValueForKey - end - %@", key);
}
结果确实有打印这两个方法

也就是说当调用
[person setValue:@20 forKeyPath:@"age"];
大概等价于下面这段代码:
[person willChangeValueForKey:@"age"];
person->age = 20;
[person didChangeValueForKey:@"age"];
KVC的赋值和取值过程是怎样的?原理是什么
setValue:forKey:的原理

简单分析一下:
- 当系统执行到
[person setValue:@10 forKeyPath:@"age"];
方法的时候,系统首先会去查找setAge:
方法,如果该方法不存在则继续去寻找_setAge:
方法,如果找到则传递参数调用方法. - 没有找到 则去查看
- (BOOL) accessInstanceVariablesDirectly
方法的返回值,其默认值就是YES,系统就会按照_age
、_isAge
、age
、isAge
顺序查找成员变量,如果找到了则直接赋值,否则执行步骤3; - 系统自动调用
setValue:forUndefinedKey:
方法并且抛出NSUnknownKeyException
的异常.
valueForKey:的原理也类似

总结:
到此我们就非常明白整个KVC的实现过程了,通过KVC修改属性值同样能触发KVO监听,说明KVC内部实现也是能够支持KVO的,所以才有网上那句话“KVO是基于KVC实现的”