zoukankan      html  css  js  c++  java
  • KVC详解

    1:使用kvc存取对象属性

    如果要更改对象属性可以通过什么方法达到呢?

    (1)通过setter和getter方法。

    (2)属性。

    (3)直接设置实例变量。

    今天学习新的一种方法:键值编码-kvc。通过指定要访问的属性名字的字符串标识符,可以进行类的属性的读取和设置。

    键值编码基本调用包括:setValue:forKey:和valueForKey。

    栗子:

    新建Student类,属性及成员变量:

    复制代码
    @interface Student : NSObject
    {
        @public
        NSString * name;
        @private
        NSString * hobby;
    }
    @property(strong,nonatomic) NSString * address;
    @property(nonatomic) int age;
    @end
    复制代码

    通过setter和getter方法、属性可以设置属性:

    复制代码
        Student * stu1 = [Student new];
        stu1->name = @"haha";
        NSLog(@"%@",stu1->name);
            
        stu1.address = @"street";    //属性可通过点语法设置
        stu1.age = 100;
        NSLog(@"address:%@,age:%d",stu1.address,stu1.age);
    复制代码

    ***下边通过键值编码kvc设置***

        Student * stu1 = [Student new];
        [stu1 setValue:@"kvcName" forKey:@"name"];
        NSLog(@"kvc设置name:%@",[stu1 valueForKey:@"name"]);
            
        [stu1 setValue:@18 forKey:@"age"];  //int基本型的需要包装成oc对象型的
        NSLog(@"kvc设置age:%@",[stu1 valueForKey:@"age"]);

    通过kvc也可以设置属性,甚至于private的成员变量也可以设置,Student类中有一个private的成员变量:hobby

        //private的属性仍然能够通过kvc设置,所以kvc破坏了面向对象的封装思想
        [stu1 setValue:@"read" forKey:@"hobby"];
        NSLog(@"%@",[stu1 valueForKey:@"hobby"]);

    既然都可以设置属性,那么kvc和其他的有什么区别呢?它的使用场景是什么呢?

    1:

    key返回NSString类型的字符串。可以单独提出来:

        Student * stu1 = [Student new];
        NSString * key = @"name";  //变量 灵活
        //[stu1 setValue:@"kvcName" forKey:@"name"];
        [stu1 setValue:@"kvcName" forKey:key];
        NSLog(@"kvc设置name:%@",[stu1 valueForKey:@"name"]);

    这样key相当于是一个中间变量,无疑大大增强了灵活性。

    2:类似id型对象不能点语法的,那么可以通过kvc设置。如:

        id obj = stu1;    //id 不能通过点语法设置属性
        obj.name = @"hehe";    //报错
        [obj setValue:@"idName" forKey:@"name"];
        NSLog(@"id设置的name:%@",[obj valueForKey:@"name"]);

    2:kvc特点:键搜索顺序

    kvc首先查找的是setter命名的属性,为证实,重写setter,如果setter被调用则可以证实。

    以Student的name成员变量为例,写setter方法:

    - (void)setName:(NSString *)newName
    {
        NSLog(@"kvc......");
        self->name = newName;
    }

    如果被调用则会打印kvc......,

    main函数中:

        Student * stu1 = [Student new];
        NSString * key = @"name";  
        [stu1 setValue:@"kvcName" forKey:key];
        NSLog(@"kvc设置name:%@",[stu1 valueForKey:@"name"]);

    打印结果:

    验证了。。。。。。如果不存在getter呢?也就是说不能用属性(属性会自动生成setter和getter方法),那么则会在对象内部查找_key或key的实例变量。

    以Student的hobby为例,它不是属性,用没有写setter方法,现在声明hobby和_hobby,为了验证写了一个test方法:

    复制代码
    @interface Student : NSObject
    {
        @public
        NSString * name;
        @private
        NSString * hobby;
        NSString * _hobby;
    }
    @property(strong,nonatomic) NSString * address;
    @property(nonatomic) int age;
    
    - (void)test;
    复制代码

    test方法:

    - (void)test
    {
        NSLog(@"_hobby = %@",self->_hobby);
        NSLog(@"hobby = %@",self->hobby);
    }

    那么当main中hobby通过kvc设置的时候,会发生什么?

        Student * stu1 = [Student new];
        [stu1 setValue:@"read" forKey:@"hobby"];
        [stu1 test];

    按说这是给hobby设置值,打印结果:

    结果_hobby上有值,这就证实kvc先查找_key。

    注意:使用@property+@synthesize可以自动生成getter和setter,但是如果为其指定非标准的getter和setter,key的搜索则会出现问题。

    2:键值编码的键路径

    设想以下情况:添加一个Brother类,Student有一个属性是Brother对象。

    复制代码
    @interface Student : NSObject
    {
        @public
        NSString * name;
        @private
        NSString * hobby;
        NSString * _hobby;
    }
    @property(strong,nonatomic) NSString * address;
    @property(nonatomic) int age;
    //添加brother属性
    @property(strong,nonatomic)Brother * brother;
    - (void)setName:(NSString *)newName;
    
    - (void)test;
    @end
    复制代码

    而Brother类下有一个属性broName。现在有这么一个需求:我需要通过Student给brother下的broName赋值。(不是通过brother)

    那么代码似乎应该这么写:

        Student * stu1 = [Student new];
        Brother * bro1 = [Brother new];
        stu1.brother = bro1;
        [stu1 setValue:@"兄弟" forKey:@"bro1.broName"];
        NSLog(@"%@",[bro1 valueForKey:@"broName"]);      //崩溃

    这么写代码崩溃,那该怎么写呢?使用键路径:setValue:forKeyPath:

    假设a对象下有一个名为b的属性,而b中包含了名称为c的属性,如果想要从a获取(或设置)c属性的内容需要使用如下路径来描述: b.c (遇到点会解析成路径)

        Student * stu1 = [Student new];
        Brother * bro1 = [Brother new];
        stu1.brother = bro1;
        [stu1 setValue:@"兄弟" forKeyPath:@"brother.broName"];
        NSLog(@"%@",[stu1 valueForKeyPath:@"brother.broName"]);

    这样就可以了,上面的代码稍作修改:现在brother下有一个brother对象brotherOld属性,该怎么赋值呢?

    @interface Brother : NSObject
    @property(strong,nonatomic) NSString * broName;
    @property(strong,nonatomic) Brother * brotherOld;
    @end

    这样相当于通过两层去设置值。

    复制代码
        Student * stu1 = [Student new];
        Brother * bro1 = [Brother new];
        stu1.brother = bro1;
            
        Brother * bigBrother = [Brother new];
        bro1.brotherOld = bigBrother;
        [stu1 setValue:@"大兄弟" forKeyPath:@"brother.brotherOld.broName"];
        NSLog(@"%@",[stu1 valueForKeyPath:@"brother.brotherOld.broName"]);
    复制代码

    3:键不存在的情况(了解即可)

    为一个不存在的键setValue forKey的话,会报错,比如:

    [stu1 setValue:@"mei you zhe ge jian" forKey:@"notExist"];    //崩溃

    可以实现setValue:forUndefinedKey:

    Student下声明一个字典:NSMutableDictionary * undefinedKeysAndValues可以存到这个字典中(相当于临时容器)

    Student.m文件

    复制代码
    //以下两个方法一般不重写
    -(void)setValue:(id)value forUndefinedKey:(NSString *)key
    {
        
        NSLog(@"呵呵,你所访问的键:%@不存在,你就别想把值:%@放到这个键上了,说的就跟你有这个键似的!",key,value);
        //[self->undefinedKeysAndValues setObject:value forKey:key];
    }
    -(id)valueForUndefinedKey:(NSString *)key
    {
        NSLog(@"对不起,没有这样的键!");
        return nil;
        //return [self->undefinedKeysAndValues objectForKey:key];
    }
    @end
    复制代码

    4:字典的valueForKey:

        NSDictionary * dict = @{@"aaa":@"11111111",@"bbb":@"22222222222"};
        //NSLog(@"%@",[dict objectForKey:@"aaa"]);
        NSLog(@"%@",[dict valueForKey:@"aaa"]);

    打印结果:

    2015-11-06 19:40:30.445 oc-kvc-001[846:61032] 11111111

    Program ended with exit code: 0

    本来字典的取值是通过objectForKey,现在也能通过valueForKey取值,说明valueForKey是NSobject的方法,字典继承自NSobject

    5:操作集合

    如果对象的某个实例变量为NSArray,而其中存放的又是对象。

    Team.h文件:

    @interface Team : NSObject
    @property(strong,nonatomic) NSArray * members;
    @end

    Student.h文件:

    @interface Student : NSObject
    @property(copy,nonatomic) NSString * name;
    @property(nonatomic) int age;
    @end

    Team下有一个属性为members的数组,members数组中存放的是Student对象。

    复制代码
            Student * s1 = [Student new];
            s1.name = @"学生甲";
            s1.age = 1000;
            
            Student * s2 = [Student new];
            s2.name = @"学生乙";
            s2.age = 28;
            
            Student * s3 = [Student new];
            s3.name = @"学生丙";
            s3.age = 1100;
            
            Student * s4 = [Student new];
            s4.name = @"学生丁";
            s4.age = 1400;
            
            NSArray * arr = @[s1,s2,s3,s4];
            
            //对NSArray请求一个键值,则会查询数组中的每一个对象来查找这个键,然后将这些结果重新打包为一个NSArray
            id ages = [arr valueForKey:@"age"];
            NSLog(@"%@",ages);
            
    //可以取最大值、最小值、求和、平均值、个数。 id maxAge = [arr valueForKeyPath:@"@max.age"]; id minAge = [arr valueForKeyPath:@"@min.age"]; id sumAge = [arr valueForKeyPath:@"@sum.age"]; id avgAge = [arr valueForKeyPath:@"@avg.age"]; id count = [arr valueForKeyPath:@"@count"]; NSLog(@"%@,%@,%@,%@,%@",maxAge,minAge,sumAge,avgAge,count); Team * team = [Team new]; team.members = arr; maxAge = [team valueForKeyPath:@"members.@max.age"]; minAge = [team valueForKeyPath:@"members.@min.age"]; sumAge = [team valueForKeyPath:@"members.@sum.age"]; avgAge = [team valueForKeyPath:@"members.@avg.age"]; count = [team valueForKeyPath:@"members.@count"]; NSLog(@"%@,%@,%@,%@,%@",maxAge,minAge,sumAge,avgAge,count);
  • 相关阅读:
    python join的用法
    python json中的 dumps loads函数
    ubuntu 初始配置
    如何为ubuntu配置java环境
    Ubuntu系统如何安装软件
    取模与取余
    基本数据类型
    js面试题——作用域和闭包
    js面试题-原型和原型链
    js面试题-变量类型和计算
  • 原文地址:https://www.cnblogs.com/liuyingjie/p/4949092.html
Copyright © 2011-2022 走看看