zoukankan      html  css  js  c++  java
  • ObjC: 使用KVC

    ObjC: 使用KVC

     LINK ADDRESS :http://blog.csdn.net/diyagoanyhacker/article/details/6918501

    KVC是什么?即:Key-Value Coding,直译是:键值编码。

    还是没明白什么意思?先看看下面的代码。

    Book类的代码,头文件:

    #import <Foundation/Foundation.h>

    @interface Book : NSObject { 
        NSString *name;

    }

    @end

    实现文件:

    #import "Book.h"

    @implementation Book

    @end

    这个Book类太简单了,只有一个实例变量name。而且,按照以前掌握的技术,没有办法给这个变量赋值了。

    下面KVC登场,在main方法中给Book实例的name属性赋值并获取该属性的值:

    int main (int argc, const char * argv[]) { 
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        Book *book=[[Book alloc] init]; 
        [book setValue:@"《Objective C入门》" forKey:@"name"]; 
        NSString *name=[book valueForKey:@"name"]; 
        NSLog(@"book name: %@",name); 
        
        [pool drain]; 
        return 0; 
    }

    这里会发现ObjC的KVC很类似Java中通过反射得到类实例变量的方式。比如valueForKey方法先尝试在Book实例上找getName方法,如果找到就调用。如果没有找到,则查找实例是否有name变量或者_name变量。如果还没找到,会抛出类似下面的异常:

    Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<Book 0x10010c730> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name1.’

    下面把代码做一点修改,首先创建了个新类Author,图书的作者,头文件:

    #import <Cocoa/Cocoa.h>

    @interface Author : NSObject { 
        NSString *name; 
    }

    @end

    也有个name属性,表示作者的姓名。实现文件什么也没写:

    #import "Author.h"

    @implementation Author

    @end

    然后,将author属性添加到Book类中,即每个Book实例都有一个author属性。头文件:

    #import <Foundation/Foundation.h>

    @class Author;

    @interface Book : NSObject { 
        NSString *name; 
        Author *author; 
    }

    @end

    实现文件还是什么都没有:

    #import "Book.h"

    @implementation Book

    @end

    在main方法中,通过kvc方式获取book的author的name属性:

    int main (int argc, const char * argv[]) { 
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        Book *book=[[[Book alloc] init] autorelease]; 
        [book setValue:@"《Objective C入门》" forKey:@"name"]; 
        NSString *name=[book valueForKey:@"name"]; 
        NSLog(@"book name: %@",name); 
        
        Author *author=[[[Author alloc] init] autorelease]; 
        [author setValue:@"Marshal Wu" forKey:@"name"]; 
        [book setValue:author forKey:@"author"]; 
        NSString *authorName=[book valueForKeyPath:@"author.name"]; 
        NSLog(@"author name: %@",authorName); 
        
        [pool drain]; 
        return 0; 
    }

    可以看到,写法很类似JSP的EL表达式:

    ${book.author.name}

    在ObjC的世界里叫Path,路径。当然,你也可以:

    [book setValue:@"zhangsan" forKeyPath:@"author.name"];

    通过路径设置属性。

    KVC还有一个很重要的特点,自动装箱拆箱功能。这在ObjC中是仅有的,其他情况下均需要使用比如NSNumber来手动拆装箱的。

    比如Book类头文件做了下面的增加:

    #import <Foundation/Foundation.h>

    @class Author;

    @interface Book : NSObject { 
        NSString *name; 
        Author *author; 
        float price; 
    }

    @end

    实现文件还是没有动,不提了。main方法增加了对price赋值和获取值的调用,使用KVC方式:

    int main (int argc, const char * argv[]) { 
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        Book *book=[[[Book alloc] init] autorelease]; 
        [book setValue:@"《Objective C入门》" forKey:@"name"]; 
        NSString *name=[book valueForKey:@"name"]; 
        NSLog(@"book name: %@",name); 
        
        Author *author=[[[Author alloc] init] autorelease]; 
        [author setValue:@"Marshal Wu" forKey:@"name"]; 
        [book setValue:author forKey:@"author"]; 
        NSString *authorName=[book valueForKeyPath:@"author.name"]; 
        NSLog(@"author name: %@",authorName); 
        
        [book setValue:@"zhangsan" forKeyPath:@"author.name"]; 
        
        [book setValue:@"10.4" forKey:@"price"]; 
        NSLog(@"book price is %@",[book valueForKey:@"price"]); 
        
        
        [pool drain]; 
        return 0; 
    }

    可以看到给price输入的是NSString类型,但是没有问题,因为KVC方式会根据字符串自动转型为适当的数值。再看打印price属性,%@是打印对象,而price属性是float基本型,这里KVC肯定做了自动装箱的处理,将基本型转为NSNumber对象。

    KVC还具备对集合的操作能力。比如,图书可以有相关图书,这是个1对多的关系。可以用集合来表示,这里用NSArray表示,在Book类的头文件中改动:

    #import <Foundation/Foundation.h>

    @class Author;

    @interface Book : NSObject { 
        NSString *name; 
        Author *author; 
        float price; 
        NSArray *relativeBooks; 
    }

    @end

    如果想得到相关图书的价格NSArray,可以使用KVC方式,见main方法:

    int main (int argc, const char * argv[]) { 
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        Book *book=[[[Book alloc] init] autorelease]; 
        [book setValue:@"《Objective C入门》" forKey:@"name"]; 
        NSString *name=[book valueForKey:@"name"]; 
        NSLog(@"book name: %@",name); 
        
        Author *author=[[[Author alloc] init] autorelease]; 
        [author setValue:@"Marshal Wu" forKey:@"name"]; 
        [book setValue:author forKey:@"author"]; 
        NSString *authorName=[book valueForKeyPath:@"author.name"]; 
        NSLog(@"author name: %@",authorName); 
        
        [book setValue:@"zhangsan" forKeyPath:@"author.name"]; 
        
        [book setValue:@"10.4" forKey:@"price"]; 
        NSLog(@"book price is %@",[book valueForKey:@"price"]); 
        
       Book *book1=[[[Book alloc] init] autorelease]; 
        [book1 setValue:@"5.0" forKey:@"price"]; 
        
        Book *book2=[[[Book alloc] init] autorelease]; 
        [book2 setValue:@"4.0" forKey:@"price"]; 
        
        NSArray *books=[NSArray arrayWithObjects:book1,book2,nil]; 
        [book setValue:books forKey:@"relativeBooks"]; 
        NSLog(@"relative books price: %@",[book valueForKeyPath:@"relativeBooks.price"]); 
        
        [pool drain]; 
        return 0; 
    }

    增加的代码见黑体斜体部分。日志将打印出相关图书的价格列表:

    2011-05-26 19:27:57.456 ReleaseMemoDemo[10042:a0f] book name: 《Objective C入门》 
    2011-05-26 19:27:57.461 ReleaseMemoDemo[10042:a0f] author name: Marshal Wu 
    2011-05-26 19:27:57.462 ReleaseMemoDemo[10042:a0f] book price is 10.4 
    2011-05-26 19:27:57.463 ReleaseMemoDemo[10042:a0f] relative books price: ( 
        5, 
        4 
    )

    KVC还能对集合做运算,比如想得到相关图书的个数、相关图书的价格总和、相关图书的平均价格、价格的最大值和价格的最小值,见下面的代码:

    NSArray *books=[NSArray arrayWithObjects:book1,book2,nil]; 
    [book setValue:books forKey:@"relativeBooks"]; 
    NSLog(@"relative books price: %@",[book valueForKeyPath:@"relativeBooks.price"]); 
    NSLog(@"relative books count: %@",[book valueForKeyPath:@"relativeBooks.@count"]); 
    NSLog(@"relative books price sum: %@",[book valueForKeyPath:@"relativeBooks.@sum.price"]); 
    NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@avg.price"]); 
    NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@max.price"]);
    NSLog(@"relative books price avg: %@",[book valueForKeyPath:@relativeBooks.@min.price]);

    相关日志:

    2011-05-26 19:45:27.786 ReleaseMemoDemo[10289:a0f] relative books price: ( 
        5, 
        4 

    2011-05-26 19:45:27.787 ReleaseMemoDemo[10289:a0f] relative books count: 2 
    2011-05-26 19:45:27.788 ReleaseMemoDemo[10289:a0f] relative books price sum: 9 
    2011-05-26 19:45:27.788 ReleaseMemoDemo[10289:a0f] relative books price avg: 4.5 
    2011-05-26 19:45:27.789 ReleaseMemoDemo[10289:a0f] relative books price avg: 5 
    2011-05-26 19:45:27.789 ReleaseMemoDemo[10289:a0f] relative books price avg: 4

    另外,如果想获得没有重复的价格集合,可以这样:

    Book *book1=[[[Book alloc] init] autorelease]; 
    [book1 setValue:@"5.0" forKey:@"price"];

    Book *book2=[[[Book alloc] init] autorelease]; 
    [book2 setValue:@"4.0" forKey:@"price"];

    Book *book3=[[[Book alloc] init] autorelease]; 
    [book3 setValue:@"4.0" forKey:@"price"];

    NSArray *books=[NSArray arrayWithObjects:book1,book2,book3,nil]; 
    [book setValue:books forKey:@"relativeBooks"];

    NSLog(@"relative books price: %@",[book valueForKeyPath:@"relativeBooks.price"]); 
    NSLog(@"relative books distinct price: %@",[book valueForKeyPath:@"relativeBooks.@distinctUnionOfObjects.price"]);

    NSLog(@"relative books count: %@",[book valueForKeyPath:@"relativeBooks.@count"]); 
    NSLog(@"relative books price sum: %@",[book valueForKeyPath:@"relativeBooks.@sum.price"]); 
    NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@avg.price"]); 
    NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@max.price"]);
    NSLog(@"relative books price avg: %@",[book valueForKeyPath:@relativeBooks.@min.price]);

    这里增加了book3实例,它的价格和book2相同。在使用@distinctUnionOfObjects后,发现效果是消除重复的价格:

    011-05-26 19:55:41.123 ReleaseMemoDemo[10378:a0f] book price is 10.4 
    2011-05-26 19:55:41.124 ReleaseMemoDemo[10378:a0f] relative books price: ( 
        5, 
        4, 
        4 

    2011-05-26 19:55:41.124 ReleaseMemoDemo[10378:a0f] relative books distinct price: ( 
        4, 
        5 
    )

    KVC还可以在一个语句中为实例的多个属性赋值:

    Book *book4=[[Book alloc] init]; 
    NSArray *bookProperties=[NSArray arrayWithObjects:@"name",@"price",nil]; 
    NSDictionary *bookPropertiesDictionary=[book4 dictionaryWithValuesForKeys:bookProperties]; 
    NSLog(@"book values: %@",bookPropertiesDictionary);

    NSDictionary *newBookPropertiesDictionary=[NSDictionary dictionaryWithObjectsAndKeys:@"《Objective C入门》",@"name", 
                                               @"20.5",@"price",nil]; 
    [book4 setValuesForKeysWithDictionary:newBookPropertiesDictionary]; 
    NSLog(@"book with new values: %@",[book4 dictionaryWithValuesForKeys:bookProperties]);

    另外,还有两个比较高级的内容:

    • nil和覆盖setNilValueForKey方法
    • 覆盖valueForUndefinedKey方法

    可自行看reference了解。

  • 相关阅读:
    使用element-ui是下拉筛选选择
    vue 组件传值
    vue element 地址联动的使用
    vux scroller
    实时监听组件中路由的变化
    vuex的使用
    对移动端滚动高度的获取
    【转】ACM 取石子问题
    【转】ACM博弈知识汇总
    EOJ 2857 编辑距离
  • 原文地址:https://www.cnblogs.com/xingchen/p/2257433.html
Copyright © 2011-2022 走看看