KVC的原理

作者: 毅想天开的小毅 | 来源:发表于2019-07-08 17:45 被阅读29次

    KVC的全称是Key-Value Coding,俗称“键值编码”,可以通过一个key来访问某个属性

    1、简单使用

    • (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
    • (void)setValue:(id)value forKey:(NSString *)key;
    • (id)valueForKeyPath:(NSString *)keyPath;
    • (id)valueForKey:(NSString *)key;

    举个例子,Person类中有一个age属性,

    @interface Person : NSObject
    @property (nonatomic, assign) int age;
    @end
    

    我们利用KVC对它进行简单的操作。

    Person *person = [[Person alloc] init];
    //赋值
    [person setValue:@(20) forKey:@"age"];
    //取值
    NSLog(@"%@",[person valueForKey:@"age"]); //打印结果为20
    

    2、KVC赋值过程

    赋值.png

    验证KCV赋值过程
    首先在Person类中添加如下几个成员变量

     @interface Person : NSObject {
        @public
        int age;
        int isAge;
        int _isAge;
        int _age;
    }
    //@property (nonatomic, assign) int age;
    @end
    

    Person.m中添加如下代码

    - (void)setAge:(int)age {
        NSLog(@"setAge: - %d", age);
    }
    
    - (void)_setAge:(int)age {
        NSLog(@"_setAge: - %d", age);
    }
    

    发现当调用KVC赋值操作[person setValue:@(20) forKey:@"age"];时,确实会调用setAge:方法,当把setAge:注释掉,就会调用_setAge:了。

    如果把这两个方法都注释掉,系统会调用,accessInstanceVariablesDirectly方法,查看是否有访问成员变量的权限。

    +(BOOL)accessInstanceVariablesDirectly {
         NSLog(@"accessInstanceVariablesDirectly");
         return NO;
    }
    

    如果返回NO,系统会调用以下方法,并抛出异常NSUnknownKeyException

    - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
        NSLog(@"setValue: forUndefinedKey:");
        [super setValue:value forUndefinedKey:key];
    }
    

    如果返回的Yes,则有权限直接访问成员变量。 系统会按照_key_isKeykeyisKey的顺序查找成员变量,并进行赋值,我们打印一下这些成员变量的值,验证一下。

    [person setValue:@(20) forKey:@"age"];
    NSLog(@"%d",person->_age);
    NSLog(@"%d",person->_isAge);
    NSLog(@"%d",person->age);
    NSLog(@"%d",person->isAge);
    
    image.png
    _age这个成员变量删掉,_isAge这个成员变量就有值了,依次是ageisAge。当把这些成员变量都删掉时,就会走setValue: forUndefinedKey:这个方法了,并抛出了NSUnknownKeyException异常。

    3、KVC取值过程

    取值流程.png

    为了验证KVC的取值过程,我们在Person.m文件中写出如下代码

    @implementation Person
    - (int)getAge {
        return 11;
    }
    
    - (int)age {
        return 12;
    }
    
    - (int)isAge {
        return 13;
    }
    
    - (int)_age {
        return 14;
    }
    
    // 默认的返回值就是YES
    + (BOOL)accessInstanceVariablesDirectly {
        return YES;
    }
    
    - (id)valueForUndefinedKey:(NSString *)key {
        NSLog(@"valueForUndefinedKey:");
        return [super valueForUndefinedKey:key];
    }
    @end
    

    分别写了如上四个get方法,并调用KVC取值方法

     NSLog(@"%@",[person valueForKey:@"age"]);
    

    发现值为11,即调用了- (int)getAge方法,将- (int)getAge方法注释掉会调用- (int)age,再然后是- (int)isAge- (int)_age。也就验证了会根据这些方法的顺序进行取值。

    - (int)getAge...这四个方法注释掉,并设置accessInstanceVariablesDirectly方法的返回值为NO,则程序会走- (id)valueForUndefinedKey:方法,并抛出NSUnknownKeyException异常

    image.png

    如果accessInstanceVariablesDirectly方法的返回值为YES,则会查找成员变量,并按照成员变量的顺序进行取值

    @implementation Person
    
    - (instancetype)init  {
        self = [super init];
        if (self) {
            _age = 11;
            _isAge = 12;
            age = 13;
            isAge = 14;
        }
        return self;
    }
    

    我们可以在person对象初始化的时候给这些成员变量赋值,然后用KVC的方式取值

     NSLog(@"%@",[person valueForKey:@"age"]);
    

    刚开始的值为11,即_age的值,后来把_age这个成员变量删掉,依次就是_isAge的值,再然后是ageisAge
    当把这些成员变量都删掉时,则系统会走- (id)valueForUndefinedKey:方法,并抛出NSUnknownKeyException异常。
    以上,也就验证了KVC的整个取值流程。

    相关文章

      网友评论

        本文标题:KVC的原理

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