zoukankan      html  css  js  c++  java
  • KVC之-(id)valueForKey:(NSString *)key的实现原理与验证

    KVC之-(id)valueForKey:(NSString *)key的实现原理与验证

     

    2.-(id)valueForKey:(NSString *)key的实现原理与验证;

    #功能:使用一个字符串类型的属性标示符,获取一个属性的值,支持普通对象和NSSet,NSArray集合对象,如果是NSArray对象就则返回不可变数组,如果是NSet就返回不可变NSSet.

    这个方法的默认实现如下:


    1.查找读访问器(getter)方法中与key相匹配的方法,匹配模式为-get<Key>, -<key>-is<Key>,按照这样的顺序如果找到一个方法,就执行它.如果返回结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber转换支持的原始类型,先把转换为NSNumber然后在返回,否则就转换为一个NSValue对象返回.


    2.否则(简单的getter方法没有找到),那么就查找接受者的类中与 -countOf<Key> and -indexIn<Key>OfObject: and -objectIn<Key>AtIndex: (NSSet类中定义的私有方法) and also -<key>AtIndexes: (对应与 -[NSOrderedSet objectsAtIndexes:]). 如果找到了-countOf<Key> , -indexIn<Key>OfObject: 以及这两个 -objectIn<Key>AtIndex:, -<key>AtIndexes: 方法中一个,那么将组合使用 -countOf<Key>, -indexIn<Key>OfObject:, -objectIn<Key>AtIndex:, and -<key>AtIndexes: 获取集合中的原始对象,然后给原始对象发送-valueForKey:消息,把结果存放入一个能够相应NSOrderedSet所有方法的集合代理对象中,返回这个代理对象.如果接收者类中实现了 -get<Key>:range: 当为获取最佳性能则使用该方法


    3.否则(1和2都没有满足),那么就接收者类中寻找名字与-countOf<Key> and -objectIn<Key>AtIndex: (定义在 NSArray 类中的私有方法) and -<key>AtIndexes: (对应与 -[NSArray objectsAtIndexes:]). 如果找到了-countOf<Key>方法 和 -objectIn<Key>AtIndex: (定义在 NSArray 类中的私有方法) 与 -<key>AtIndexes: 中的其中一个,那么,通过 -countOf<Key>, -objectIn<Key>AtIndex:, and -<key>AtIndexes: 给原始对象发送 -valueForKey:消息,把返回结果放入一个能够响应NSArray所有方法的代理对象中,然后返回该代理对象.如果接收器的类对象实现,一个名字与-get<Key>:range:.匹配可选方法,那么为了最好性能讲使用该方法.


    4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>,-enumeratorOf<Key>, and -memberOf<Key>: (对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:这三个方法,给原始对象发送-valueForKey:消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象中,然后返回这个代理对象


    5.否则(1,2,3,4都没有满足[没有简单的访问器方法也没有集合的访问器方法]),如果接受器类的 +accessInstanceVariablesDirectly 方法返回YES,依照匹配模式 _<key>, _is<Key>, <key>, or is<Key> 去匹配实例变量名称,如果有一个实例变量找到了,就返回这个实例变量的值,如果结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber转换支持的原始类型,先把转换为NSNumber然后在返回,否则就转换为一个NSValue对象返回.


    6.否则(1,2,3,4,5都没有满足[没有简单的访问器方法,没有集合访问方法,没有实例变量]),那么将会执行-valueForUndefinedKey:方法,默认产生一个NSUndefinedKeyException的异常,但是你可以重写该方法.


    验证

    Person.h

    typedef struct {
        int day;
        int month;
        int year;
    } Date;
    
    @interface Person : NSObject
    {
        NSString *_name;
        int _age;
        Date *birthday;
    //    NSString *address;
    //    NSString *_address;
    //    NSString *isAddress;
         NSString *_isAddress;
    
    }
    
    @property (nonatomic, copy) NSString *name;
    
    @property (nonatomic,assign) int age;
    
    
    @end

    Person.m

    @implementation Person
    
    - (int) age
    {
        NSLog(@"%s------%d",__func__,_age);
        return _age;
    }
    
    
    - (NSString *) name
    {
        NSLog(@"%s----------%@",__func__,_name);
        return _name;
    }
    
    @end

    验证

    1.查找读访问器(getter)方法中与key相匹配的方法,匹配模式为-get<Key>, -<key>, or -is<Key>,按照这样的顺序如果找到一个方法,就执行它.
    修改name的getter方法:

    - (NSString *) getName
    {
        NSLog(@"%s----------%@",__func__,_name);
        return _name;
    }

    输出结果为:

    2015-08-15 23:53:16.771 company[1253:77763] -[Person getName]----------小明

    再次修改name的getter方法::

    - (NSString *) name
    {
        NSLog(@"%s----------%@",__func__,_name);
        return _name;
    }

    输出结果

    2015-08-15 23:54:36.060 company[1275:78595] -[Person name]----------小明

    再次修改name的getter方法:

    - (NSString *) isName
    {
        NSLog(@"%s----------%@",__func__,_name);
        return _name;
    }

    输出结果,为空,也就是说根本没有执行isName方法
    修改name属性的声明为

    @property (nonatomic, copy,getter=isName) NSString *name;

    输出结果为

    2015-08-15 23:58:59.271 company[1387:81935] -[Person isName]----------小明

    2.如果返回结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber转换支持的原始类型,先把转换为NSNumber然后在返回,否则就转换为一个NSValue对象返回.

    测试代码

        p.name = @"小明";
        NSString *name = [p valueForKey:@"name"];
        NSLog(@"%@",[name class]);
        NSLog(@"%@",name);
    
        p.age = 10;
        NSNumber *age = [p valueForKey:@"age"];
        NSLog(@"%@",[age class]);
        NSLog(@"%@",age);
    
        p.birthday = (Date){1990,5,1};
        NSValue *birthday = [p valueForKey:@"birthday"];
        NSLog(@"%@",[birthday class]);
        NSLog(@"%@",birthday);

    输出结果

    2015-08-16 00:03:03.420 company[1440:84446] -[Person isName]----------小明
    2015-08-16 00:03:03.421 company[1440:84446] __NSCFConstantString
    2015-08-16 00:03:03.421 company[1440:84446] 小明
    2015-08-16 00:03:03.421 company[1440:84446] -[Person age]------10
    2015-08-16 00:03:03.421 company[1440:84446] __NSCFNumber
    2015-08-16 00:03:03.421 company[1440:84446] 10
    2015-08-16 00:03:03.421 company[1440:84446] NSConcreteValue
    2015-08-16 00:03:03.421 company[1440:84446] <c6070000 05000000 01000000>

    3.否则(简单的getter方法没有找到),那么就查找接受者的类中与 -countOf<Key> and -indexIn<Key>OfObject: and -objectIn<Key>AtIndex: (NSSet类中定义的私有方法) and also -<key>AtIndexes: (对应与 -[NSOrderedSet objectsAtIndexes:]). 如果找到了-countOf<Key> , -indexIn<Key>OfObject: 以及这两个 -objectIn<Key>AtIndex:, -<key>AtIndexes: 方法中一个,那么将组合使用 -countOf<Key>, -indexIn<Key>OfObject:, -objectIn<Key>AtIndex:, and -<key>AtIndexes: 获取集合中的原始对象,然后给原始对象发送-valueForKey:消息,把结果存放入一个能够相应NSOrderedSet所有方法的集合代理对象中,返回这个代理对象.如果接收者类中实现了 -get<Key>:range: 当为获取最佳性能则使用该方法

    此处我使用系统自带的类进行验证

    测试代码:
        Person *p = [[Person alloc] init];
        p.name = @"小明";
        Person *p1 = [[Person alloc] init];
        [p1 setValue:@"小明" forKey:@"name"];
        Person *p2 = [[Person alloc] init];
        [p2 setValue:@"小花" forKey:@"name"];
        Person *p3 = [[Person alloc] init];
        [p3 setValue:@"happy" forKey:@"name"];
    
         NSOrderedSet *set = [[NSOrderedSet alloc] initWithObjects:p1,p2,p3, nil];
        NSOrderedSet *names =  [set valueForKey:@"name"];
        NSLog(@"%@",[names class]);
        NSLog(@"%@",names);

    输出结果:

    2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------小明
    2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------小花
    2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------happy
    2015-08-16 00:35:39.134 company[1687:109689] __NSOrderedSetI
    2015-08-16 00:35:39.134 company[1687:109689] {(
        "U5c0fU660e",
        "U5c0fU82b1",
        happy
    )}

    这里的names 类为一个能够响应NSOrderedSet所有方法的的代理对象,请注意这里所说的代理对象,与代理设计模式不是同一个概念,
    这里代理就相当于中介,比如你想租某个房东租房子,但是你找不到这个房东,只能通过房产中介,传递信息给房东,这个中介
    就可以理解为代理对象


    4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>,-enumeratorOf<Key>, and -memberOf<Key>: (对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:这三个方法,给原始对象发送-valueForKey:消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象中,然后返回这个代理对象

    测试代码:
        Person *p = [[Person alloc] init];
        p.name = @"小明";
        Person *p1 = [[Person alloc] init];
        [p1 setValue:@"小明" forKey:@"name"];
        Person *p2 = [[Person alloc] init];
        [p2 setValue:@"小花" forKey:@"name"];
        Person *p3 = [[Person alloc] init];
        [p3 setValue:@"happy" forKey:@"name"];
    
        NSArray *set = [[NSArray alloc] initWithObjects:p1,p2,p3, nil];
        NSArray *names =  [set valueForKey:@"name"];
        NSLog(@"%@",[names class]);
        NSLog(@"%@",names);

    输出结果:

    2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------小明
    2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------小花
    2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------happy
    2015-08-16 00:33:21.741 company[1670:108148] __NSArrayI
    2015-08-16 00:33:21.741 company[1670:108148] (
        "U5c0fU660e",
        "U5c0fU82b1",
        happy
    )

    这里的names 类为一个能够响应NSArray所有方法的的代理对象


    4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>,-enumeratorOf<Key>, and -memberOf<Key>: (对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:这三个方法,给原始对象发送-valueForKey:消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象中,然后返回这个代理对象

    测试代码:
        Person *p1 = [[Person alloc] init];
        [p1 setValue:@"小明" forKey:@"name"];
        Person *p2 = [[Person alloc] init];
        [p2 setValue:@"小花" forKey:@"name"];
        Person *p3 = [[Person alloc] init];
        [p3 setValue:@"happy" forKey:@"name"];
    
    
        NSSet *set = [[NSSet alloc] initWithObjects:p1,p2,p3, nil];
        NSSet *names =  [set valueForKey:@"name"];
        NSLog(@"%@",[names class]);
        NSLog(@"%@",names);

    输出结果:

    2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------happy
    2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------小花
    2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------小明
    2015-08-16 00:29:50.879 company[1646:106351] __NSSetI
    2015-08-16 00:29:50.879 company[1646:106351] {(
        happy,
        "U5c0fU660e",
        "U5c0fU82b1"
    )}

    这里的names 类为一个能够响应NSSet所有方法的的代理对象


    5.否则(1,2,3,4都没有满足[没有简单的访问器方法也没有集合的访问器方法]),如果接受器类的 +accessInstanceVariablesDirectly 方法返回YES,依照匹配模式 _<key>, _is<Key>, <key>, or is<Key> 去匹配实例变量名称,如果有一个实例变量找到了,就返回这个实例变量的值,如果结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber转换支持的原始类型,先把转换为NSNumber然后在返回,否则就转换为一个NSValue对象返回.

    Person类中,分别使用下面address进行测试

    @interface Person : NSObject
    {
    //    NSString *address;
    //    NSString *_address;
    //    NSString *isAddress;
         NSString *_isAddress;
    
    }

    测试代码

        Person *p = [[Person alloc] init];
        [p setValue:@"金燕龙大厦" forKey:@"address"];
        NSString *address = [p valueForKey:@"address"];
        NSLog(@"%@",address);

    输出结果均为

    2015-08-16 00:41:16.387 company[1776:113187] 金燕龙大厦

    6.否则(1,2,3,4,5都没有满足[没有简单的访问器方法,没有集合访问方法,没有实例变量]),那么将会执行-valueForUndefinedKey:方法,默认产生一个NSUndefinedKeyException的异常,但是你可以重写该方法.

         NSString *工具 = [p valueForKey:@"工具"];
         NSLog(@"工具=%@",工具);

    运行结果: 产生一个NSUnknownKeyException的异常

    Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x7fb2b2727b50> valueForUndefinedKey:]: this class is not key value coding-compliant for the key 工具.'

    在Person.m文件中重写-valueForUndefinedKey: 方法

    - (id)valueForUndefinedKey:(NSString *)key
    {
        NSLog(@"%s",__func__);
        NSLog(@"key == %@",key);
        return @"黄瓜";
    }

    再次运行程序输出结果为:

    2015-08-16 00:53:01.568 company[1841:119255] -[Person valueForUndefinedKey:]
    2015-08-16 00:53:01.568 company[1841:119255] key == 工具
    2015-08-16 00:53:01.568 company[1841:119255] 工具=黄瓜
  • 相关阅读:
    JVM三部曲之运行时数据区 (第一部)
    c++鼠标点点,获取坐标值,放入到txt文件中
    自己实现的SVM源码
    SVM资料
    caffe源码解析
    caffe调试小结2
    caffe中卷积层和pooling层计算下一层的特征map的大小
    gpu对任意长度的矢量求和
    caffe代码调试小结
    caffe添加自己的层
  • 原文地址:https://www.cnblogs.com/LiLihongqiang/p/6704602.html
Copyright © 2011-2022 走看看