美文网首页
OC语法:KVC的底层实现

OC语法:KVC的底层实现

作者: 意一ineyee | 来源:发表于2019-10-02 13:25 被阅读0次
    一、KVC是什么
    二、怎么使用KVC
    三、KVC的底层实现
    四、KVC常见面试题

    一、KVC是什么


    KVC全称Key-Value Coding,翻译过来是键值编码,是一种通过一个key来访问某个属性的机制。

    解释一下,我们通常不都是通过settergetter方法来访问公开属性的嘛,私有属性更是访问不到,而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:还可以用来访问当前对象的属性的属性,并可以这样一层一层套下去,也就是说KeyPathKey这种方式要强大一些。

    举个简单例子:

    // 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的底层实现


    KeyPathKey是同理的,下面以Key方式为例。

    1、KVC的赋值流程

    setValue:forKey:方法内部:

    • 第一波,查找setter方法

    首先会把参数key的第一个字母变成大写的。

    然后根据这个Key按顺序去查找对应的setter方法,包括setKey:_setKey:,如果找到了对应的setter方法,就调用并把参数value传进去,从而修改掉属性的值(本质上还是修改掉了成员变量的值)。

    • 第二波,直接给成员变量赋值

    如果没有找到对应的setter方法,就会去查看accessInstanceVariablesDirectly方法的返回值(能否直接访问成员变量,默认返回YES),如果不能直接访问成员变量,就会报一个“找不到key”的错。

    如果能直接访问成员变量,就会按顺序去查找对应的成员变量,_key_isKeykeyisKey,如果找到了对应的成员变量,就直接给成员变量赋值(若设置了KVO的话,此时还会触发KVO,因为KVC在此处也调用了willChangeValueForKey:didChangeValueForKey:方法),如果没有找到对应的成员变量,就会报一个“找不到key”的错。

    2、KVC的取值流程

    valueForKey:方法内部:

    • 第一波,查找getter方法

    首先会把参数key的第一个字母变成大写的。

    然后根据这个Keykey按顺序去查找对应的setter方法,包括getKeykey:isKey:_key:,如果找到了对应的getter方法,就调用并返回属性的值(本质上还是读取成员变量的值)。

    • 第二波,直接读取成员变量的值

    如果没有找到对应的getter方法,就会去查看accessInstanceVariablesDirectly方法的返回值(能否直接访问成员变量,默认返回YES),如果不能直接访问成员变量,就会报一个“找不到key”的错。

    如果能直接访问成员变量,就会按顺序去查找对应的成员变量,包括_key_isKeykeyisKey,如果找到了对应的成员变量,就直接读取成员变量的值,如果没有找到对应的成员变量,就会报一个“找不到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"];
    

    相关文章

      网友评论

          本文标题:OC语法:KVC的底层实现

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