美文网首页
iOS -- KVC高级使用以及赋值过程分析

iOS -- KVC高级使用以及赋值过程分析

作者: 可惜你不是我的双子座 | 来源:发表于2019-01-17 15:06 被阅读25次

    一、KVC赋值过程分析

    1.1. 先找相关方法 set<Key>: _set<Key>: setIs<Key>:
    1.2. 若没有相关方法+ (BOOL)accessInstanceVariablesDirectly判断是否可以直接访问成员变量(默认为YES
    1.3. 如果是判断是NO,直接执行KVCsetValue:forUndefinedKey:(系统抛出一个异常,未定义key)
    1.4. 如果是YES,继续找相关变量_<key> _is<Key> <key> is<Key>
    1.5. 方法或成员都不存在,setValue: forUndefinedKey:方法,默认是抛出异常
    示例1

    // ZYPerson.h
    @interface ZYPerson : NSObject{
    
         @public
        NSString* _name; ④
        NSString* _isName; ⑤
        NSString* name; ⑥
        NSString* isName; ⑦
    }
    
    #import "ZYPerson.h"
    
    // ZYPerson.m
    @implementation ZYPerson
    
    - (void) setName:(NSString*) name {   ①
        NSLog(@"%s", __func__);
    }
    
    - (void) _setName:(NSString*) name {  ②
        NSLog(@"%s", __func__);
    }
    
    - (void) setIsName:(NSString*) name {  ③
        NSLog(@"%s", __func__);
    }
    
    - (NSString*) getName { ⑧
        NSLog(@"%s", __func__);
        return @"getName";
    }
    
    - (NSString*) name { ⑨
        NSLog(@"%s", __func__);
        return @"name";
    }
    
    @end
    
    // ViewController.m
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        ZYPerson *p = [ZYPerson new];
        
        [p setValue:@"tom" forKey:@"name"];
        NSLog(@"name = %@", [p valueForKey:@"name"]);
        NSLog(@"name = %@", p->name);
        NSLog(@"_name = %@", p->_name);
        NSLog(@"isName = %@", p->isName);
        NSLog(@"_isName = %@", p->_isName);
    }
    
    @end
    

    说明 ① ② ③ 等代码中标注

    如果①存在,② ③ 可无,结果如下

    2019-01-16 15:46:56.798680+0800 KVC001[20175:1536368] -[ZYPerson setName:]
    2019-01-16 15:46:56.798798+0800 KVC001[20175:1536368] name = (null)
    2019-01-16 15:46:56.798881+0800 KVC001[20175:1536368] _name = (null)
    2019-01-16 15:46:56.798955+0800 KVC001[20175:1536368] isName = (null)
    2019-01-16 15:46:56.799023+0800 KVC001[20175:1536368] _isName = (null)
    

    如果①不存在,② 存在,③ 可无,结果如下

    2019-01-16 15:49:26.650198+0800 KVC001[20220:1539317] -[ZYPerson _setName:]
    2019-01-16 15:49:26.650333+0800 KVC001[20220:1539317] name = (null)
    2019-01-16 15:49:26.650412+0800 KVC001[20220:1539317] _name = (null)
    2019-01-16 15:49:26.650482+0800 KVC001[20220:1539317] isName = (null)
    2019-01-16 15:49:26.650548+0800 KVC001[20220:1539317] _isName = (null)
    

    如果① ②不存在,③ 存在,结果如下

    2019-01-16 15:50:19.981200+0800 KVC001[20246:1540579] -[ZYPerson setIsName:]
    2019-01-16 15:50:19.981313+0800 KVC001[20246:1540579] name = (null)
    2019-01-16 15:50:19.981399+0800 KVC001[20246:1540579] _name = (null)
    2019-01-16 15:50:19.981475+0800 KVC001[20246:1540579] isName = (null)
    2019-01-16 15:50:19.981541+0800 KVC001[20246:1540579] _isName = (null)
    

    如果① ② ③ 不存在,结果如下

    2019-01-16 15:57:59.461807+0800 KVC001[20377:1549518] name = (null)
    2019-01-16 15:57:59.461938+0800 KVC001[20377:1549518] _name = tom
    2019-01-16 15:57:59.462019+0800 KVC001[20377:1549518] isName = (null)
    2019-01-16 15:57:59.462112+0800 KVC001[20377:1549518] _isName = (null)
    

    如果① ② ③ ④不存在,结果如下

    2019-01-16 15:59:34.276391+0800 KVC001[20413:1551470] name = (null)
    2019-01-16 15:59:34.276510+0800 KVC001[20413:1551470] isName = (null)
    2019-01-16 15:59:34.276587+0800 KVC001[20413:1551470] _isName = tom
    

    以此类推...
    结论: 先找相关方法 set<Key>: _set<Key>: setIs<Key>: 再找相关变量_<key> _is<Key> <key> is<Key>

    二、KVC取值过程分析

    2.1. 先找相关方法get<Key> key
    2.2. 若没有相关方法 + (BOOL)accessInstanceVariablesDirectly,判断是否可以直接方法成员变量(默认是YES
    2.3. 如果是判断是NO,直接执行KVCvalueForUndefinedKey:(系统抛出一个异常,未定义key)
    2.4. 如果是YES,继续找相关变量_<key> _is<Key> <key> is<Key>
    2.5. 方法或成员都不存在,valueForUndefinedKey:方法,默认是抛出异常

    如果⑧存在,⑨可无,结果如下

    2019-01-16 16:18:45.537583+0800 KVC001[20710:1573310] -[ZYPerson getName]
    2019-01-16 16:18:45.537709+0800 KVC001[20710:1573310] name = getName
    2019-01-16 16:18:45.537797+0800 KVC001[20710:1573310] name = (null)
    2019-01-16 16:18:45.537869+0800 KVC001[20710:1573310] isName = (null)
    2019-01-16 16:18:45.537941+0800 KVC001[20710:1573310] _isName = tom
    

    如果⑧ 不存在,⑨存在,结果如下

    2019-01-16 16:20:15.113493+0800 KVC001[20744:1575289] -[ZYPerson name]
    2019-01-16 16:20:15.113627+0800 KVC001[20744:1575289] name = name
    2019-01-16 16:20:15.113708+0800 KVC001[20744:1575289] name = (null)
    2019-01-16 16:20:15.113778+0800 KVC001[20744:1575289] isName = (null)
    2019-01-16 16:20:15.114321+0800 KVC001[20744:1575289] _isName = tom
    

    如果⑧ ⑨不存在,结果如下

    2019-01-16 16:22:04.841822+0800 KVC001[20792:1578120] name = tom
    2019-01-16 16:22:04.841938+0800 KVC001[20792:1578120] name = (null)
    2019-01-16 16:22:04.842009+0800 KVC001[20792:1578120] _name = tom
    2019-01-16 16:22:04.842078+0800 KVC001[20792:1578120] isName = (null)
    2019-01-16 16:22:04.842144+0800 KVC001[20792:1578120] _isName = (null)
    

    以此类推...
    结论: 先找相关方法get<Key> key 再找相关变量_<key> _is<Key> <key> is<Key>

    三、KVC异常处理

    示例2

    // ZYPerson.h
    @interface ZYPerson : NSObject
    
    @property (nonatomic, copy) NSString *name;
    
    @property (nonatomic, assign) NSInteger age;
    
    @end
    
    // ViewController.m
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        ZYPerson *p = [ZYPerson new];
        
        [p setValue:nil forKey:@"age"];
        [p setValue:@"tom" forKey:@"name1"];
        NSLog(@"name = %@", [p valueForKey:@"name1"]);
    }
    
    @end
    
    3.1、值为空
    2019-01-16 16:46:44.065665+0800 KVC001[21151:1602368] *** Terminating app due to uncaught 
    exception 'NSInvalidArgumentException',
     reason: '[<ZYPerson 0x600000dfe360> setNilValueForKey]: 
    could not set nil as the value for the key age.'
    *** First throw call stack:
    
    3.2、key值不存在
    3.2.1、赋值key值不存在
    2019-01-16 17:05:38.681366+0800 KVC001[21451:1622468] *** Terminating app due to uncaught 
    exception 'NSUnknownKeyException', r
    eason: '[<ZYPerson 0x6000001664e0> setValue:forUndefinedKey:]: 
    this class is not key value coding-compliant for the key name1.'
    *** First throw call stack:
    
    3.2.2、取值key值不存在
    2019-01-16 17:16:26.100903+0800 KVC001[21604:1634947] *** Terminating app due to uncaught
     exception 'NSUnknownKeyException', 
    reason: '[<ZYPerson 0x600002d09300> valueForUndefinedKey:]: this class is not
     key value coding-compliant for the key name1.'
    *** First throw call stack:
    

    为了程序的健壮性,重写上面报错方法,如下:

    // ZYPerson.m
    @implementation ZYPerson
    
    // 对非对象类型,值不能为空
    - (void) setNilValueForKey:(NSString *)key {
        NSLog(@"%@ 值不能为空", key);
    }
    
    // 赋值key值不存在
    - (void) setValue:(id)value forUndefinedKey:(NSString *)key {
        NSLog(@"key = %@值不存在 ", key);
    }
    
    // 取值key值不存在
    - (id) valueForUndefinedKey:(NSString *)key {
        NSLog(@"key=%@不存在", key);
        return nil;
    }
    
    @end
    

    结果如下:

    2019-01-16 17:38:24.760843+0800 KVC001[21893:1654601] age 值不能为空
    2019-01-16 17:38:24.761428+0800 KVC001[21893:1654601] key = name1值不存在 
    2019-01-16 17:38:24.761934+0800 KVC001[21893:1654601] key=name1不存在
    2019-01-16 17:38:24.762044+0800 KVC001[21893:1654601] name = (null)
    

    四、KVC正确性验证

    场景:假设人的年纪不能小于0或者不能大于200

    // ViewController.m
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        ZYPerson *p = [ZYPerson new];
        
        NSNumber* value = @200;
        if ([p validateValue:&value forKey:@"age" error:NULL]) {
            [p setValue:value forKey:@"age"];
        }
    }
    
    // ZYPerson.m
    @implementation ZYPerson
    
    - (BOOL) validateAge:(inout id  _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError {
        
        NSNumber* value = (NSNumber*)*ioValue;
        if ([value integerValue] <= 0 || [value integerValue] >= 200) {
            
            return NO;
        }
        return YES;
    }
    
    @end
    

    validateValue方法的工作原理:
    4.1. 先找一下你的类中是否实现了方法-(BOOL)validate<Key>:error:
    4.2. 如果实现了就会根据实现方法里面的自定义逻辑返回NO或者YES如果没有实现这个方法,则系统默认返回就是YES

    五、KVC与字典

    利用这两个API

    /* Given an array of keys, return a dictionary containing the keyed attribute values, to-one-related objects, and/or collections of to-many-related objects. Entries for which -valueForKey: returns nil have NSNull as their value in the returned dictionary.
    */
    - (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
    
    /* Given a dictionary containing keyed attribute values, to-one-related objects, and/or collections of to-many-related objects, set the keyed values. Dictionary entries whose values are NSNull result in -setValue:nil forKey:key messages being sent to the receiver.
    */
    - (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;
    

    示例3

    // KVC字典操作
    - (void) dictionaryTest {
        ZYPerson* p = [ZYPerson new];
        
        NSDictionary* dict = @{
                               @"name":@"Tom",
                               @"age":@18,
                               @"nick":@"Cat",
                               @"height":@180
                               };
        
        [p setValuesForKeysWithDictionary:dict];
        NSLog(@"p.name = %@, p.age = %d, p.nick =%@, p.height = %f", p.name, p.age, p.nick, p.height);
        
        NSArray* keys = @[@"name", @"age"];
        NSDictionary* dict1 = [p dictionaryWithValuesForKeys:keys];
        
        NSLog(@"%@", dict1);
    }
    
    @end
    

    输出如下:

    2019-01-16 21:09:31.140841+0800 KVC001[24763:1926331] p.name = Tom, p.age = 18, p.nick =Cat, p.height = 180.000000
    2019-01-16 21:09:31.141081+0800 KVC001[24763:1926331] {
        age = 18;
        name = Tom;
    }
    

    六、KVC的消息传递

    1.length 字符串长度
    2.lowercaseString 全部小写
    3.uppercaseString 全部大写
    4.capitalizedString 首字母大写

    示例4

    // KVC消息传递  array
    - (void) arrayKVCTest {
       
        NSArray* arr = @[@"monDay", @"tuesDay", @"wednesDay"];
        NSArray* lengthArr = [arr valueForKey:@"length"];
        NSLog(@"%@", lengthArr);
        
        NSArray* lowercaseArr = [arr valueForKey:@"lowercaseString"]; 
        NSLog(@"%@", lowercaseArr);
        
        NSArray* uppercaseArr = [arr valueForKey:@"uppercaseString"]; 
        NSLog(@"%@", uppercaseArr);
        
        NSArray* capitalizedArr = [arr valueForKey:@"capitalizedString"];
        NSLog(@"%@", capitalizedArr);
    }
    
    @end
    

    结果如下:

    2019-01-16 21:44:56.079415+0800 KVC001[25267:2056897] (
        6,
        7,
        9
    )
    2019-01-16 21:44:56.079665+0800 KVC001[25267:2056897] (
        monday,
        tuesday,
        wednesday
    )
    2019-01-16 21:44:56.079798+0800 KVC001[25267:2056897] (
        MONDAY,
        TUESDAY,
        WEDNESDAY
    )
    2019-01-16 21:44:56.079937+0800 KVC001[25267:2056897] (
        Monday,
        Tuesday,
        Wednesday
    )
    

    七、KVC容器操作

    7.1聚合操作符

    1.@avg 平均值
    2.@count 数量
    3.@max 最大值
    4.@min 最小值
    5.@sum 总和

    - (void) contrainerTest {
        
        NSMutableArray* students = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
        
        NSLog(@"%@", [students valueForKey:@"height"]);
        
        // 平均身高
        float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
        NSLog(@"%f", avg);
    }
    

    结果如下:

    2019-01-16 22:05:20.043606+0800 KVC001[25551:2194029] (
        "1.75",
        "1.65",
        "1.75",
        "1.65",
        "1.65",
        "1.65"
    )
    2019-01-16 22:05:20.045527+0800 KVC001[25551:2194029] 1.683333
    

    操作符取值格式如下:

    官网文档
    7.2 数组操作符

    1.@distinctUnionOfObjects 去重
    2.@unionOfObjects 不去重

    - (void) contrainerArrayTest {
        
        NSMutableArray* students = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            ZYPerson* student = [ZYPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
        
        NSLog(@"%@", [students valueForKey:@"height"]);
        
        NSArray* arr = [students valueForKeyPath:@"@distinctUnionOfObjects.height"];
        NSLog(@"arr = %@", arr);
        
        NSArray* arr1 = [students valueForKeyPath:@"@unionOfObjects.height"];
        NSLog(@"arr1 = %@", arr1);
    }
    

    结果如下:

    2019-01-16 22:12:13.543600+0800 KVC001[25671:2242372] (
        "1.73",
        "1.71",
        "1.67",
        "1.65",
        "1.65",
        "1.73"
    )
    2019-01-16 22:12:13.543815+0800 KVC001[25671:2242372] arr = (
        "1.65",
        "1.73",
        "1.71",
        "1.67"
    )
    2019-01-16 22:12:13.543964+0800 KVC001[25671:2242372] arr1 = (
        "1.73",
        "1.71",
        "1.67",
        "1.65",
        "1.65",
        "1.73"
    )
    

    7.3嵌套集合(array&set)操作

    1.@distinctUnionOfArrays 数组去重
    2.@unionOfArrays 数组不去重
    3.@distinctUnionOfSets 集合去重

    - (void) containerNestingTest {
        
        NSMutableArray* students = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
        
        NSMutableArray* students1 = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students1 addObject:student];
        }
        
        // 嵌套数组
        NSArray* nestArr = @[students, students1];
        
        
        NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.height"];
        NSLog(@"arr = %@", arr);
        
        NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.height"];
        NSLog(@"arr1 = %@", arr1);
        
    }
    
    - (void) containerNestingTest1 {
        
        NSMutableSet* students = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students addObject:student];
        }
        
        NSLog(@"students = %@", [students valueForKey:@"height"]);
        
        NSMutableSet* students1 = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            TZPerson* student = [TZPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"height":@(1.65 + 0.02*arc4random_uniform(6)),
                                   };
            [student setValuesForKeysWithDictionary:dict];
            [students1 addObject:student];
        }
        
         NSLog(@"students1 = %@", [students1 valueForKey:@"height"]);
        
        NSSet* nestSet = [NSSet setWithObjects:students, students1, nil];
        
        NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.height"];
        NSLog(@"arr1 = %@", arr1);
    }
    
    

    八、KVC集合代理对象

    通过一个key映射多个方法

    // TZPerson.h
    @interface TZPerson : NSObject
    
    @property (nonatomic, assign) NSUInteger count;
    
    @property (nonatomic, strong) NSMutableArray *penArr;
    
    @end
    
    // TZPerson.m
    @implementation TZPerson
    
    - (NSUInteger) countOfBooks {
        return self.count;
    }
    
    - (id) objectInBooksAtIndex:(NSUInteger)index {
        return [NSString stringWithFormat:@"book %lu", index];
    }
    
    // 个数
    - (NSUInteger) countOfPens {
        return [self.penArr count];
    }
    
    // 是否包含这个成员对象
    - (id) memberOfPens:(id)object {
        return [self.penArr containsObject:object] ? object : nil;
    }
    
    // 迭代器
    - (id) enumeratorOfPens {
        return [self.penArr objectEnumerator];
    }
    
    @end
    
    // ViewController.h
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        TZPerson* p = [TZPerson new];
        p.count = 5;
        
        NSLog(@"books = %@", [p valueForKey:@"books"]);
        
        p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
        NSSet* set = [p valueForKey:@"pens"];
        NSLog(@"pens = %@", set);
    }
    
    

    结果如下

    2019-01-17 15:04:27.768932+0800 KVC001[4017:619765] books = (
        "book 0",
        "book 1",
        "book 2",
        "book 3",
        "book 4"
    )
    2019-01-17 15:04:27.769168+0800 KVC001[4017:619765] pens = {(
        pen0,
        pen1,
        pen2,
        pen3
    )}
    

    相关文章

      网友评论

          本文标题:iOS -- KVC高级使用以及赋值过程分析

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