• iOS KVC 键值编码


    1 什么是键值编码
    键值编码,key value coding, 简称KVC
    KVC, 通过字符串间接的获取、改变对象的状态。
    2 KVC的基本使用

    通过字符串获取对象的状态

    接口 oc对象的实例方法:- (id)valueForKey:(NSString *)key;
    实例 找name属性或者name方法 如果找不到,就找name和_name实例变量NSString *name = [car valueForKey:@”name”];

    通过字符串设置对象的状态

    接口 oc对象的实例方法:- (void)setValue:(id)value forKey:(NSString *)key;
    实例 找name属性或者setName方法 如果找不到,就找name和_name实例变量 [car setValue:@”北京” forKey:@”name”];

    car.h

    struct size {
        float length, width, height;
    };
    
    @interface Car : NSObject
    {
        NSString *serial;  //出厂编号
    }
    
    @property (nonatomic, copy) NSString *name; //品牌
    @property (nonatomic) int years;
    @property (nonatomic) CGRect rect;
    @property (nonatomic, readonly) struct size  size;
    
    @end

    car.m

    @implementation Car
    
    - (instancetype) init
    {
        self = [super init];
        if (self) {
            _name = @"BYD";
            _years = 3;
            serial = @"12346789";
            _rect = CGRectMake(10, 20, 30, 40);
    
            _size.length = 5.0;
            _size.width = 1.8;
            _size.height = 1.7;
        }
        return self;
    }
    
    @end

    main.m

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Car *car = [[Car alloc] init];
    
            //找name属性或者name方法
            NSString *name = [car valueForKey:@"name"]; 
            NSLog(@"%@", name);
    
            //找serial属性和serial方法都找不到
            //就找serial和_serail实例变量
            NSString *serial = [car valueForKey:@"serial"];
            NSLog(@"%@", serial);
    
            //valueForKey读取的值如果是标量类型(比如int,float等),则会自动装箱,返回NSNumber类型
            NSNumber *years = [car valueForKey:@"years"];
            NSLog(@"%d", [years intValue]);
    
            //valueForKey读取的值如果是结构体类型, 则会自动装箱成NSValue类型
            //常用结构体类型,可以使用对应的方法直接拆箱,比如rectValue
            NSValue *rect = [car valueForKey:@"rect"];
            CGRect rect2 = [rect rectValue];
            NSLog(@"%.2f, %.2f, %.2f, %.2f", 
                 rect2.origin.x, rect2.origin.y, rect2.size.width, rect2.size.height);
    
            //valueForKey读取的值如果是结构体类型, 则会自动装箱成NSValue类型
            //自定义的结构体类型,可以使用通用的getValue:方法拆箱
            NSValue *size = [car  valueForKey:@"size"];
            struct size size2;
            [size getValue:&size2];
            NSLog(@"%.2f, %.2f, %.2f", size2.length, size2.width, size2.height);
    
            //设置
            [car setValue:@"北京" forKey:@"name"];
            NSLog(@"%@", car.name);
    
            //设置标量值时,参数需要自己装箱
            //在setValue:forKey:方法内部,会自动把表示值的参数进行拆箱
            [car setValue:[NSNumber numberWithInt:4] forKey:@"years"];
            NSLog(@"%d", car.years);
        }
        return 0;
    }

    3 键路径

    接口 -(id)valueForKeyPath:(NSString *)keyPath;
    实例 NSString *engineName = [car valueForKeyPath:@”engine.name”];

    4 整体操作
    键路径中,如果某个属性是一个集合类型(比如,数组、字典),则路径其后的部分将分别作用于该集合中的每一个对象。

    接口 1)valueForKeyPath: 对集合中的每个成员发送getter消息,并返回一个数组 2)setValue: forKeyPath:对集合中的每个成员发送setter消息
    实例 NSArray *pressures = [car valueForKeyPath:@”tires.pressure”];[car setValue:@15 forKeyPath:@”tires.pressure”];

    5 使用KVC实现快速运算
    @count
    对@count左侧的的值统计总数, 返回NSNumber类型

    接口 在valueForKeyPath:中的键路径中使用@count
    实例 NSNumber *number = [car valueForKeyPath:@”tires.@count”];NSLog(@”tire count = %d”, [number intValue]);

    @sum

    接口 在valueForKeyPath:中的键路径中使用@sum
    实例 NSNumber *pressureSum = [car valueForKeyPath:@”tires.@sum.pressure”]; NSLog(@”pressureSum = %2.f”, [pressureSum floatValue]);

    @avg
    与 @sum类似,但是计算的是平均值

    接口 在valueForKeyPath:中的键路径中使用@avg
    实例 NSNumber *pressureAvg = [car valueForKeyPath:@”tires.@avg.pressure”];NSLog(@”tire pressureAvg = %2.f”, [pressureAvg floatValue]);

    @max
    与 @sum类似,但是计算的是最大值。
    @min
    与 @sum类似,但是计算的是最大值。
    @distinctUnionOfObjects
    返回一个数组,重复值只取一个。

    接口 在valueForKeyPath:中的键路径中使用@ distinctUnionOfObjects
    实例 NSArray *array2 = [car valueForKeyPath:@”tires.@distinctUnionOfObjects.name”];NSLog(@”%@”, array2); //{3M}

    6 使用KVC实现批处理
    批处理获取多个属性的值,返回一个字典

    接口
      (NSDictionary )dictionaryWithValuesForKeys:(NSArray )keys;
    实例 NSArray *keys = @[@”name”, @”years”];NSDictionary *values = [car dictionaryWithValuesForKeys:keys];NSLog(@”%@”, values);

    批处理设置多个属性的值.

    接口
      (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;
    实例 NSDictionary *dict = @{@”name”:@”BMW”, @”years”:@”5”};[car setValuesForKeysWithDictionary:dict];NSLog(@”%@, %d”, car.name, car.years);

    7 处理未定义的键
    使用KVC时,如果指定的键或键路径,没有直到对应的属性、方法或实例变量,则会抛出异常而终止。

    解决方案:
    使用valueForUndefinedKey:和setValue:forUndefinedKey:
    对所有未定义的键做统一处理。

    原理:
    添加一个可变字典实例变量,
    当遇到未定义的键时,将会调用对象的valueForUndefinedKey:方法,
    在该方法内,把用户设置的未定义的键、值都保存到这个字典中。
    这样就可以对该对象使用任意的键了。

    demo
    car.h

    @interface Car : NSObject
    {
        NSMutableDictionary *_stuff;
    }
    @end

    car.m

    - (void)setValue:(id)value forUndefinedKey:(NSString *)key
    {
        //对_stuff实现惰性初始化,因为一般情况下不会使用未定义的键
        if (_stuff == nil) {
            _stuff = [[NSMutableDictionary alloc] init];
        }
        _stuff[key] = value;
    }
    
    - (id)valueForUndefinedKey:(NSString*)key
    {
        return _stuff[key];
    }

    main.m

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Car *car = [[Car alloc] init];
    
            //直接使用未定义的键
            [car setValue:@2700 forKey:@"weight"];   
            NSNumber *weight = [car valueForKey:@"weight"];
            NSLog(@"%d kg", [weight intValue]);
        }
        return 0;
    }
  • 相关阅读:
    Pretty girl,你一定要去旅行
    难受就哭,开心就笑
    你对你的大学生活满意吧
    [leetCode]575. 分糖果
    [leetCode]383.赎金信
    242. 有效的字母异位词
    [leetCode]538. 把二叉搜索树转换为累加树
    [leetCode]面试题 02.07. 链表相交
    [leetCode]206. 反转链表
    [leetCode]707. 设计链表
  • 原文地址:https://www.cnblogs.com/Sico2Sico/p/5384190.html
Copyright © 2020-2023  润新知