iOS底层之KVC

作者: 恍然如梦_b700 | 来源:发表于2020-07-05 00:20 被阅读0次

    在讲KVC之前我们先来了解几个概念

    iOS 成员变量,实例变量,属性的区别

    • 成员变量
      一个类里面所有的变量都是成员变量
    • 实例变量
      实例创建出来的变量,比如Class
      id 不一定,id是特殊的Class
    • 属性
      属性一般会有一个默认的setter+getter方法
      我们知道苹果早期的编译器是GCC,后来变成了LLVM
      经过LLVM,如果发现没有匹配到实例变量的属性的时候,就会自动创建一个_<key>
    @interface Person : NSObject{
        @public
        NSString *myName; //成员
        id hell; // id - > class
        int age;
    }
    @property (nonatomic, copy) NSString *name;
    

    KVC取值赋值原理

    为什么要介绍上面三个东西,因为KVC就是在上面这三个基础上操作的
    KVC的源码并没有开源,所以我们学习KVC的方法就是去查看苹果的官方文档
    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/SearchImplementation.html#//apple_ref/doc/uid/20000955-CJBBBFFA

    通过官方文档我们可以看出:
    KVC是通过<key>间接访问成员变量的一种机制
    通过键值编码
    这里我简单翻译了一下关键的部分

    Getter的搜索方式

    1. 搜索实例与像一个名字找到的第一个访问方法get<Key><key>is<Key>,或者_<key>,按照这个顺序。如果找到,则调用它并执行步骤5。否则,继续下一步。

    2. 如果未找到简单的访问器方法,请在实例中搜索名称与模式countOf<Key>objectIn<Key>AtIndex:(与NSArray该类定义的原始方法<key>AtIndexes:相对应)和(与该[NSArray](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/Foundation/Classes/NSArrayClassCluster/Description.html#//apple_ref/occ/cl/NSArray)方法相对应)相匹配的方法objectsAtIndexes:

      如果找到其中的第一个以及其他两个中的至少一个,则创建一个响应所有NSArray方法的集合代理对象并将其返回。否则,请继续执行步骤3。

      代理对象随后将任何NSArray接收到的一些组合的消息countOf<Key>objectIn<Key>AtIndex:<key>AtIndexes:消息给键-值编码创建它兼容的对象。如果原始对象还实现了名称为的可选方法get<Key>:range:,则代理对象也会在适当时使用该方法。实际上,代理对象与与键值编码兼容的对象一起使用,可以使基础属性的行为就好像它是NSArray,即使不是。

    3. 如果没有找到简单的访问方法或阵列访问方法组,寻找一个三重的方法命名countOf<Key>enumeratorOf<Key>memberOf<Key>:(对应于由所定义的原始的方法[NSSet](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/Foundation/Classes/NSSetClassCluster/Description.html#//apple_ref/occ/cl/NSSet)类)。

      如果找到所有三个方法,请创建一个响应所有NSSet方法的集合代理对象并将其返回。否则,请继续执行步骤4。

      随后,此代理对象将NSSet收到的任何消息转换为,和消息的某种组合countOf<Key>,以创建该对象的对象。实际上,代理对象与与键值编码兼容的对象一起使用,可以使基础属性的行为就好像它是,即使不是。 enumeratorOf<Key>``memberOf<Key>:``NSSet

    4. 如果发现收集的访问方法没有简单的存取方法或者组,如果接收器的类方法[accessInstanceVariablesDirectly]返回YES,搜索名为实例变量_<key>_is<Key><key>,或者is<Key>,按照这个顺序。如果找到,请直接获取实例变量的值,然后继续执行步骤5。否则,请继续执行步骤6。

    5. 如果检索到的属性值是对象指针,则只需返回结果。

      如果该值是所支持的标量类型NSNumber,则将其存储在NSNumber实例中并返回它。

      如果结果是NSNumber不支持的标量类型,请转换为NSValue对象并返回该对象。

    6. 如果其他所有方法均失败,请调用[valueForUndefinedKey:]。默认情况下会引发异常,但是的子类NSObject可能提供特定于键的行为。

    Setter

    setValue:forKey: 使用以下步骤

    1. 按此顺序查找第一个名为set<Key>:or_set<Key>的访问器。如果找到,请使用输入值(或根据需要解包的值)调用它并完成。

    2. 如果没有实现上面两个set<Key>:or_set<Key>,如果类方法accessInstanceVariablesDirectly返回YES(默认),寻找一个实例变量与名称类似_<key>_is<Key><key>,或者is<Key>,按照这个顺序。如果找到,直接用输入值(或展开值)设置变量并完成操作。

    3. 在找不到访问器或实例变量时,调用setValue:forUndefinedKey:。默认情况下会引发异常,但是的子类NSObject可能提供特定于键的行为。

    如果你不了解这个流程,你就会很迷惑。

    流程图:

    image.png

    KVC的延伸应用

    KVC还有一些比较有意思的东西

    #pragma mark - array取值
    - (void)arrayDemo{
        
        NSLog(@"******************array取值****************************");
    
        LGPerson *p = [LGPerson new];
        p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
        NSArray *arr = [p valueForKey:@"pens"];
        NSLog(@"pens = %@", arr);
        //NSLog(@"%@",arr[0]);
        NSLog(@"%d",[arr containsObject:@"pen1"]);
        // 遍历
        NSEnumerator *enumerator = [arr objectEnumerator];
        NSString *str = nil;
        while (str = [enumerator nextObject]) {
            NSLog(@"%@", str);
        }
    }
    
    #pragma mark - 字典操作
    
    - (void)dictionaryTest{
        
        NSLog(@"******************KVC字典转模型****************************");
        
        NSDictionary* dict = @{
                               @"name":@"Fuye",
                               @"nick":@"KC",
                               @"subject":@"iOS",
                               @"age":@18,
                               @"length":@180
                               };
        LGPerson *p = [[LGPerson alloc] init];
        // 字典转模型
        [p setValuesForKeysWithDictionary:dict];
        NSLog(@"%@",p);
        // 键数组转模型到字典
        NSArray *array = @[@"name",@"age"];
        NSDictionary *dic = [p dictionaryWithValuesForKeys:array];
        NSLog(@"%@",dic);
    }
    
    #pragma mark - KVC消息传递
    - (void)arrayMessagePass{
        NSLog(@"******************KVC消息传递****************************");
    
        NSArray *array = @[@"Rose",@"Fuye",@"Natasy",@"ChangE"];
        NSArray *lenStr= [array valueForKeyPath:@"length"];
        NSLog(@"%@",lenStr);// 消息从array传递给了string
        NSArray *lowStr= [array valueForKeyPath:@"lowercaseString"];
        NSLog(@"%@",lowStr);
    }
    
    #pragma mark - 聚合操作符
    // @avg、@count、@max、@min、@sum
    - (void)aggregationOperator{
        
        NSLog(@"******************@avg、@count、@max、@min、@sum****************************");
    
        NSMutableArray *personArray = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            LGPerson *p = [LGPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"length":@(180 + 2*arc4random_uniform(6)),
                                   };
            [p setValuesForKeysWithDictionary:dict];
            [personArray addObject:p];
        }
        NSLog(@"%@", [personArray valueForKey:@"length"]);
        
        /// 平均身高
        float avg = [[personArray valueForKeyPath:@"@avg.length"] floatValue];
        NSLog(@"%f", avg);
        
        int count = [[personArray valueForKeyPath:@"@count.length"] intValue];
        NSLog(@"%d", count);
        
        int sum = [[personArray valueForKeyPath:@"@sum.length"] intValue];
        NSLog(@"%d", sum);
        
        int max = [[personArray valueForKeyPath:@"@max.length"] intValue];
        NSLog(@"%d", max);
        
        int min = [[personArray valueForKeyPath:@"@min.length"] intValue];
        NSLog(@"%d", min);
    }
    
    // 数组操作符 @distinctUnionOfObjects @unionOfObjects
    - (void)arrayOperator{
        NSLog(@"****************** @distinctUnionOfObjects @unionOfObjects****************************");
        NSMutableArray *personArray = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            LGPerson *p = [LGPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"length":@(175 + 2*arc4random_uniform(6)),
                                   };
            [p setValuesForKeysWithDictionary:dict];
            [personArray addObject:p];
        }
        NSLog(@"%@", [personArray valueForKey:@"length"]);
        // 返回操作对象指定属性的集合
        NSArray* arr1 = [personArray valueForKeyPath:@"@unionOfObjects.length"];
        NSLog(@"arr1 = %@", arr1);
        // 返回操作对象指定属性的集合 -- 去重
        NSArray* arr2 = [personArray valueForKeyPath:@"@distinctUnionOfObjects.length"];
        NSLog(@"arr2 = %@", arr2);
        
    }
    
    // 嵌套集合(array&set)操作 @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets
    - (void)arrayNesting{
        
        NSLog(@"******************@distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets****************************");
        
        NSMutableArray *personArray1 = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            LGPerson *person = [LGPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"length":@(175 + 2*arc4random_uniform(6)),
                                   };
            [person setValuesForKeysWithDictionary:dict];
            [personArray1 addObject:person];
        }
        
        NSMutableArray *personArray2 = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            LGPerson *person = [LGPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"length":@(175 + 2*arc4random_uniform(6)),
                                   };
            [person setValuesForKeysWithDictionary:dict];
            [personArray2 addObject:person];
        }
        
        // 嵌套数组
        NSArray* nestArr = @[personArray1, personArray2];
        
        NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.length"];
        NSLog(@"arr = %@", arr);
        
        NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.length"];
        NSLog(@"arr1 = %@", arr1);
    }
    
    - (void)setNesting{
        
        NSLog(@"******************setNesting****************************");
    
        NSMutableSet *personSet1 = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            LGPerson *person = [LGPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"length":@(175 + 2*arc4random_uniform(6)),
                                   };
            [person setValuesForKeysWithDictionary:dict];
            [personSet1 addObject:person];
        }
        NSLog(@"personSet1 = %@", [personSet1 valueForKey:@"length"]);
        
        NSMutableSet *personSet2 = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            LGPerson *person = [LGPerson new];
            NSDictionary* dict = @{
                                   @"name":@"Tom",
                                   @"age":@(18+i),
                                   @"nick":@"Cat",
                                   @"length":@(175 + 2*arc4random_uniform(6)),
                                   };
            [person setValuesForKeysWithDictionary:dict];
            [personSet2 addObject:person];
        }
        NSLog(@"personSet2 = %@", [personSet2 valueForKey:@"length"]);
    
        // 嵌套set
        NSSet* nestSet = [NSSet setWithObjects:personSet1, personSet2, nil];
        
        // 交集
        NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.length"];
        NSLog(@"arr1 = %@", arr1);
    }
    

    如果不阅读官方文档,很难想象KVC还有这么有意思的东西可以玩一玩,可以通过KVC做很多事,所以对于底层的探索是每一个开发者都应该去做的。

    相关文章

      网友评论

        本文标题:iOS底层之KVC

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