美文网首页iOSios进阶
探索iOS底层原理第三篇——KVC

探索iOS底层原理第三篇——KVC

作者: 经天纬地 | 来源:发表于2019-05-11 14:43 被阅读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方法:


image.png

从上述结果看通过KVC修改属性值确实能调用KVO属性监听.上一篇我们讲到KVO实际上会调用willChangeValueForKeydidChangeValueForKey方法,我们再验证一下是否通过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:的原理

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

valueForKey:的原理也类似

image.png

总结:

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

相关文章

网友评论

    本文标题:探索iOS底层原理第三篇——KVC

    本文链接:https://www.haomeiwen.com/subject/yafxaqtx.html