美文网首页
OC底层探索20、KVC 原理

OC底层探索20、KVC 原理

作者: _zhang__ | 来源:发表于2020-10-29 21:49 被阅读0次

    Key-Value Coding Programming Guide 苹果文档
    KVC: Key-Value Coding

    image.png

    一、KVC 的简单使用

    1、普通赋值

    1.1、setter -- llvm
        MyPerson *person = [[MyPerson alloc] init];
        person.name      = @"setName";
        person.age       = 18;
        person->myName   = @"yName";
        NSLog(@"%@ - %d - %@",person.name,person.age,person->myName);
    // 输出:setName - 18 - yName
    
    1.2、Key-Value Coding KVC
    [person setValue:@"张三" forKey:@"name"];
    

    2、集合类型

        person.array = @[@"1",@"2",@"3"];
        // 修改数组
        // person.array[0] = @"100";
        // 1: 建一个新的数组 - KVC 赋值
        NSArray *array = [person valueForKey:@"array"];
        array = @[@"100",@"2",@"3"];
        [person setValue:array forKey:@"array"];
        NSLog(@"%@",[person valueForKey:@"array"]);
        // 输出:100 2 3
        // 2
        NSMutableArray *mArray = [person mutableArrayValueForKey:@"array"];
        mArray[0] = @"200";
        NSLog(@"%@",[person valueForKey:@"array"]);
        // 输出:200 2 3
    

    3、访问非对象属性

    以结构体为例:

    // 结构体
    typedef struct {
        float x, y, z;
    } ThreeFloats;
    
    
        ThreeFloats floats = {1.,2.,3.};
        NSValue *value     = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
        [person setValue:value forKey:@"threeFloats"];// 设置
        NSValue *value1    = [person valueForKey:@"threeFloats"];// 取值
        NSLog(@"%@",value1);
        // 输出:{length = 12, bytes = 0x0000803f0000004000004040}
        ThreeFloats th;
        [value1 getValue:&th];
        NSLog(@"%.2f-%.2f-%.2f",th.x,th.y,th.z);
        // 输出:1.00-2.00-3.00
    

    4、keyPath

        MyStudent *student = [MyStudent alloc];
        student.subject    = @"subStr吗";
        person.student     = student;
        [person setValue:@"subStr啊" forKeyPath:@"student.subject"];
        NSLog(@"%@",[person valueForKeyPath:@"student.subject"]);
        // 输出:subStr啊
    

    5、数组Array的操作

    #pragma mark - array 取值
    - (void)arrayDemo{
        MyStudent *p = [MyStudent new];
        p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
        NSArray *arr = [p valueForKey:@"penArr"]; // 动态成员变量
        NSLog(@"pens = %@", arr);
        /* pens = (
            pen0,
            pen1,
            pen2,
            pen3
        )*/
        //NSLog(@"%@",arr[0]);
        NSLog(@"%d",[arr containsObject:@"pen9"]);// 0 false
        // 遍历
        NSEnumerator *enumerator = [arr objectEnumerator];
        NSString* str = nil;
        while (str = [enumerator nextObject]) {
            NSLog(@"%@", str);
        }
    }
    

    6、字典Dictionary

    - (void)dictionaryDemo {
        
        // 1:
        NSDictionary *dict = @{
                               @"name":@"李四",
                               @"nick":@"小四",
                               @"subject":@"iOS",
                               @"age":@18,
                               @"length":@180
                               };
        MyStudent *p = [[MyStudent alloc] init];
        // 字典转模型
        [p setValuesForKeysWithDictionary:dict];
        /**
          (lldb) p *$0
          (NSDictionary) $1 = {
            [0] = {
              key = 0x000000010585e3e0 @"age"
              value = 0x947567b2920d34b5 (int)18
            }
            [1] = {
              key = 0x000000010585e3a0 @"subject"
              value = 0x000000010585e3c0 @"iOS"
            }
            [2] = {
              key = 0x000000010585e360 @"nick"
              value = 0x000000010585e380 @"小四"
            }
            [3] = {
              key = 0x000000010585e0c0 @"name"
              value = 0x000000010585e340 @"李四"
            }
            [4] = {
              key = 0x000000010585e400 @"length"
              value = 0x947567b2920d3ed5 (int)180
            }
          }
        */
    
    
        // 2:
        // 数组 转 模型 到 字典
        NSArray *array = @[@"name",@"age"];
        NSDictionary *dict2 = [p dictionaryWithValuesForKeys:array];
        NSLog(@"%@",dict2);
        /**
        (lldb) p *$2
        (NSDictionary) $3 = {
          [0] = {
            key = 0x000000010585e0c0 @"name"
            value = 0x000000010585e340 @"李四"
          }
          [1] = {
            key = 0x000000010585e3e0 @"age"
            value = 0x947567b2920d34b5 (int)18
          }
        }
        */
    }
    

    7、KVC 消息传递

    - (void)arrayMessagePass{
        NSArray *array = @[@"hank",@"anmyli",@"kod",@"CC"];
        NSArray *lenStr= [array valueForKeyPath:@"length"];
        NSLog(@"%@",lenStr);// 消息从 array 传递给了 string
        // string.length
    //    (
    //        4,
    //        6,
    //        3,
    //        2
    //    )
        NSArray *nStr= [array valueForKeyPath:@"uppercaseString"];// lowercaseString uppercaseString
        NSLog(@"%@",nStr);// 字母大写
        // 输出:
    //    (
    //        HANK,
    //        ANMYLI,
    //        KOD,
    //        CC
    //    )
    }
    

    8、NSSet

    8.1)setKVC
    - (void)setNesting{
    
    //    NSSet *s = [NSSet setWithArray:@[@"1",@"3",@"8",@"4"]];
    //    NSLog(@"%@",s);// 
        
        NSMutableSet *personSet1 = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            MyStudent *person = [MyStudent 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"]);
    //    输出:
    //    personSet1 = {(
    //        175,
    //        181,
    //        177,
    //        179
    //    )}
    
        NSMutableSet *personSet2 = [NSMutableSet set];
        for (int i = 0; i < 6; i++) {
            MyPerson *person = [MyPerson new];
            NSDictionary* dict = @{
                @"name":@"jerry",
                @"age":@(18+i),
    //            @"nick":@"Cat",
    //            @"length":@(175 + 2*arc4random_uniform(6)),
            };
            [person setValuesForKeysWithDictionary:dict];
            [personSet2 addObject:person];
        }
        NSLog(@"personSet2 = %@", [personSet2 valueForKey:@"age"]);
    //    输出:
    //    personSet2 = {(
    //        21,
    //        20,
    //        23,
    //        19,
    //        22,
    //        18
    //    )}
    
        // 嵌套set
        NSSet* nestSet = [NSSet setWithObjects:personSet1, personSet2, nil];
        // 交集
        NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.name"];
        NSLog(@"arr1 = %@", arr1);
    //    输出:
    //    arr1 = {(
    //        Tom,
    //        jerry
    //    )}
    }
    
    8.2)setarray 区别:

    1、代码调试
    执行如下代码:

        NSMutableSet *personSet1 = [NSMutableSet set];
        NSMutableArray *personArr1 = [NSMutableArray array];
        for (int i = 0; i < 6; i++) {
            MyStudent *person = [MyStudent new];
            NSDictionary* dict = @{
                @"name":@"Tom",
                @"age":@(18+i),
                @"nick":@"Cat",
                @"length":@(175 + 2*arc4random_uniform(6)),
            };
            [person setValuesForKeysWithDictionary:dict];
            [personSet1 addObject:person];
            [personArr1 addObject:person];
        }
    

    打个断点在 for 所在行,调试信息如下:

    (lldb) p malloc_size((__bridge void *)personArr1)
    (size_t) $0 = 48
    (lldb) p malloc_size((__bridge void *)personSet1)
    (size_t) $1 = 32
    (lldb) 
    

    继续执行,并调试:

    (lldb) po personArr1
    <__NSArrayM 0x600003e33f60>(
    <MyStudent: 0x600003e307e0>,
    <MyStudent: 0x600003e33f90>,
    <MyStudent: 0x600003e33fc0>,
    <MyStudent: 0x600003e30750>,
    <MyStudent: 0x600003e324f0>,
    <MyStudent: 0x600003e308a0>
    )
    
    (lldb) po personSet1
    {(
        <MyStudent: 0x600003e33fc0>,
        <MyStudent: 0x600003e33f90>,
        <MyStudent: 0x600003e30750>,
        <MyStudent: 0x600003e324f0>,
        <MyStudent: 0x600003e307e0>,
        <MyStudent: 0x600003e308a0>
    )}
    

    8.2)官方文档 OC 下 - Values and Collections

    NSSet - NSSet :

    image.png

    NSArray:

    image.png

    这里把字典的图示也放在这里便于比较,Dictionary:

    image.png

    setarray主要区别:
    array有序,数据可重复;
    set无序,数据不重复。

    二、KVC 原理探究 基于苹果官方文档

    Accessor Search Patterns 文档地址

    1、Search Pattern for the Basic Setter

    image.png

    setter 的流程:

    1. 先找set<Key>: or _set<Key>
    2. 若没找到且accessInstanceVariablesDirectly returns YES,则继续找_<key>, _is<Key>, <key>, or is<Key>;
    3. 没找着则: invoke setValue:forUndefinedKey:.。默认情况下会引发一个异常,但NSObject的子类可能提供特定于键的行为

    2、Search Pattern for the Basic Getter

    image.png

    getter流程:

    1. 实例中搜索,先找 get<Key>, <key>, is<Key>, or _<key>,找到了就调用去·5·中处理结果;没找着则继续下一步;
    2. 搜索名称与模式 countOf<Key>objectIn<Key>AtIndex: (对应于NSArray类定义的基本方法) 以及 <key>AtIndexes: (对应于NSArray 方法 objectsAtIndexes:);
      2.1.、如果找到第1个或至少两个中的一个,则创建一个集合代理对象,该对象响应所有NSArray方法并返回该方法。否则,继续执行步骤3;
      2.2、代理对象随后将接收到的任何NSArray消息转换为countOf<Key>objectIn<Key>AtIndex:<Key> AtIndexes:的一些组合,这些组合将消息发送给创建它的符合键值编码的对象。如果原始对象还实现了一个名为get<Key>:range:的可选方法,代理对象也会在适当的时候使用它。实际上,代理对象与键值编码兼容的对象一起工作,允许底层属性像NSArray一样工作,即使它不是NSArray
    3. 如果没有找到简单的访问方法或数组访问方法组,查找下面三个方法,countOf<Key>enumeratorOf<Key>,memberOf<Key>:(对应于NSSet类定义的原语方法)。
      3.1、如果这三个方法都找到了,创建一个集合代理对象,它响应所有NSSet方法并返回那个。否则,继续执行步骤4。
      3.2、这个代理对象随后将它接收到的任何NSSet消息转换为countOf<Key>enumeratorOf<Key>memberOf<Key>:消息的组合,并发送给创建它的对象。实际上,代理对象与遵循键值编码的对象一起工作,允许底层属性像NSSet一样运行,即使它不是NSSet
    4. 如果没有找到简单的访问方法或集合访问方法组,并且如果接收方的类方法accessinstancevariables返回YES,那么按照顺序搜索一个实例变量:_<key>_is< key><key>,或is< key>。如果找到,直接获取实例变量的值并继续执行步骤5。否则,继续执行步骤6。
    5. 如果检索到的属性值是一个对象指针,只需返回结果。
      如果值是NSNumber支持的标量类型,将其存储在NSNumber实例中并返回。
      如果结果是NSNumber不支持的标量类型,转换为NSValue对象并返回它。
    6. 如果所有其他方法都失败,则调用valueForUndefinedKey:。这在默认情况下会引发一个异常,但是NSObject的一个子类可能提供特定于键的行为。

    3、Getting/Setting Attribute Values Using Keys

    对应的 Apple 地址

    image.png image.png

    更多信息在苹果文档中比较详细,这里不再多做介绍。

    三、自定义 KVC

    相关文章

      网友评论

          本文标题:OC底层探索20、KVC 原理

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