KVC详解

作者: lmfei | 来源:发表于2019-11-20 10:20 被阅读0次

    KVC定义

    KVC(Key-value coding)键值编码。通过<Key>来直接访问对象的属性,或者为属性赋值,而不是通过明确的set/get方法,这样就可以再运行时动态的访问和修改对象的属性,而不是在编译时确定,它是iOS开发的黑魔法之一,它是很多黑魔法的基础。

    注:KVC与点语法有什么区别?
    在实现了set\get方法的类中,两者没有太大差异。但是在没有实现set\get方法的类中,点语法无法使用,这时KVC的优势就体现出来了。

    KVC的使用

    KVC的定义都是对NSObject的扩展来实现的,所以对于所有继承NSObject的类都能使用KVC。
    常用的API

    //通过key取值
    - (nullable id)valueForKey:(NSString *)key;
    //通过key设值
    - (void)setValue:(nullable id)value forKey:(NSString *)key;
    //通过keyPath取值
    - (nullable id)valueForKeyPath:(NSString *)keyPath;
    //通过keyPath设值
    - (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
    //验证属性值是否满足条件
    - (BOOL)validateValue:(inout id _Nullable * _Nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
    //取值时如果key不存在,则会调用这个方法,抛出异常
    - (nullable id)valueForUndefinedKey:(NSString *)key;
    //设值时如果key不存在,则会调用这个方法,抛出异常
    - (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
    //如果设值时,该属性值不支持nil,则会调用这个方法
    - (void)setNilValueForKey:(NSString *)key;
    
    setValue:forKey流程图
    setValue_forKey

    流程图概述如下:

    • 优先查找set<key>:方法,如果找到执行方法,如果没有找到,再查找_set<key>,如果找到执行方法,如果没有找到,执行下一步
    • 检查accessInstanceVariablesDirectly的返回值,如果返回NO,则执行setValue:forUndefinedKey:抛出异常,如果返回YES,默认返回YES,执行下一步
    • 按照以下顺序,依次查找成员变量:_<key>、_is<key>、<key>、is<key>,找到则赋值,如果任未找到则调用setValue:forUndefinedKey:然后抛出异常
    valueForKey:流程图
    valueForKey流程图

    流程图概述如下:

    • 首先按get<key>、<key>、is<key>的顺序查找getter方法,找到直接调用,没有找到执行下一步
    • 查找countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes方法,如果countOf<Key>和另外两个方法中的一个被找到,那么就会返回一个NSArray集合,否则执行下一步
    • 查找countOf<Key>,enumeratorOf<Key>,memberOf<Key>方法,如果三个方法都找到,那么就返回一个NSSet集合
    • 检查accessInstanceVariablesDirectly的返回值,如果返回NO,则执行valueforUndefinedKey:抛出异常,如果返回YES,默认返回YES,执行下一步
    • 按照以下顺序,依次查找成员变量:_<key>、_is<key>、<key>、is<key>,找到则取值,如果任未找到则调用valueforUndefinedKey:然后抛出异常
    KVC处理异常
    nil异常

    在设值时,给值类型传了nil,则会调用setNilValueForKey:,默认抛出异常,
    重写该方法则不抛出异常

    undefinedKey异常

    在设值时对不存在的key进行了操作,会报错setValue: forUndefinedKey:,重写该方法避免崩溃

    KVC使用场景
    1. 动态地存取值
    2. 访问和修改私有变量、修改控件的内部属性
    [self.textField setValue:[UIColor orangeColor] forKeyPath:@"_placeholderLabel.textColor"];
    
    1. model和字典之间的转换
    - (void)kvc_dict {
        //modal->dict
        LPersona *per = [LPersona new];
        [per setValue:@10 forKey:@"age"];
        [per setValue:@170 forKey:@"height"];
        [per setValue:@70 forKey:@"weight"];
        [per setValue:@"M" forKey:@"hex"];
    
        NSArray *arr = @[@"age", @"height", @"weight", @"hex"];
        NSDictionary *dict = [per dictionaryWithValuesForKeys:arr];
        NSLog(@"modal->dict:%@",dict);
        
        //dict->moal
        LPersona *per2 = [LPersona new];
        NSDictionary *tempDic = @{@"age":@17, @"height":@175, @"weight":@75, @"hex":@"W"};
        [per2 setValuesForKeysWithDictionary:tempDic];
        NSLog(@"age:%@, height:%@, weight:%@, hex:%@",[per2 valueForKey:@"age"],[per2 valueForKey:@"height"],[per2 valueForKey:@"weight"],[per2 valueForKey:@"hex"]);
    }
    
    1. 操作集合
    - (void)kvc_group {
        LPersona *p1 = [LPersona new];
        [p1 setValue:@10 forKey:@"age"];
        [p1 setValue:@"M" forKey:@"hex"];
    
        LPersona *p2 = [LPersona new];
        [p2 setValue:@20 forKey:@"age"];
        [p2 setValue:@"W" forKey:@"hex"];
    
        LPersona *p3 = [LPersona new];
        [p3 setValue:@30 forKey:@"age"];
        [p3 setValue:@"M" forKey:@"hex"];
    
        LPersona *p4 = [LPersona new];
        [p4 setValue:@40 forKey:@"age"];
        [p4 setValue:@"W" forKey:@"hex"];
    
        LPersona *p5 = [LPersona new];
        [p5 setValue:@10 forKey:@"age"];
        [p5 setValue:@"M" forKey:@"hex"];
    
        //简单集合运算
        NSArray *arr = @[p1,p3,p2,p5,p4];
        NSNumber *sum = [arr valueForKeyPath:@"@sum.age"];
        NSLog(@"sum:%@",sum);
        
        NSNumber *avg = [arr valueForKeyPath:@"@avg.age"];
        NSLog(@"avg:%@",avg);
        
        NSNumber *count = [arr valueForKeyPath:@"@count.age"];
        NSLog(@"count:%@",count);
    
        NSNumber *min = [arr valueForKeyPath:@"@min.age"];
        NSLog(@"min:%@",min);
    
        NSNumber *max = [arr valueForKeyPath:@"@max.age"];
        NSLog(@"max:%@",max);
        
        //去重
        NSArray *distinct = [arr valueForKeyPath:@"@distinctUnionOfObjects.age"];
        NSLog(@"distinctUnionOfObjects-----------");
        for (NSNumber *age in distinct) {
            NSLog(@"%f", age.floatValue);
        }
        
        //全集
        NSArray *arrUnion = [arr valueForKeyPath:@"@unionOfObjects.age"];
        NSLog(@"unionOfObjects-----------");
        for (NSNumber *age in arrUnion) {
            NSLog(@"%f", age.floatValue);
        }
    }
    

    打印结果:

    2019-11-19 11:05:29.556863+0800 KVC探究一[2925:7723768] sum:110
    2019-11-19 11:05:29.557050+0800 KVC探究一[2925:7723768] avg:22
    2019-11-19 11:05:29.557138+0800 KVC探究一[2925:7723768] count:5
    2019-11-19 11:05:29.557219+0800 KVC探究一[2925:7723768] min:10
    2019-11-19 11:05:29.557292+0800 KVC探究一[2925:7723768] max:40
    2019-11-19 11:05:29.557715+0800 KVC探究一[2925:7723768] distinctUnionOfObjects-----------
    2019-11-19 11:05:29.557903+0800 KVC探究一[2925:7723768] 10.000000
    2019-11-19 11:05:29.557957+0800 KVC探究一[2925:7723768] 20.000000
    2019-11-19 11:05:29.558009+0800 KVC探究一[2925:7723768] 30.000000
    2019-11-19 11:05:29.558126+0800 KVC探究一[2925:7723768] 40.000000
    2019-11-19 11:05:29.558337+0800 KVC探究一[2925:7723768] unionOfObjects-----------
    2019-11-19 11:05:29.558518+0800 KVC探究一[2925:7723768] 10.000000
    2019-11-19 11:05:29.559370+0800 KVC探究一[2925:7723768] 30.000000
    2019-11-19 11:05:29.559471+0800 KVC探究一[2925:7723768] 20.000000
    2019-11-19 11:05:29.559601+0800 KVC探究一[2925:7723768] 10.000000
    2019-11-19 11:05:29.559654+0800 KVC探究一[2925:7723768] 40.000000

    1. KVO
      强大的KVO,竟然是基于KVC的

    生活如此美好,今天就点到为止。。。

    相关文章

      网友评论

          本文标题:KVC详解

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