Coco学习之KVC
来源:互联网 发布:淘宝人工售后客服 编辑:程序博客网 时间:2024/06/04 23:26
首先,什么是KVC,KVC的全称是KeyValueCoding,也叫键值编码。KVC的用途是给使用者提供一种通过属性名称获取和改变属性值的方法,属性名称就是Key,属性的值就是Value。这里,属性的名称是以字符串变量的方式提供的,能够根据字符串变量来调用相应的方法,就为程序提供的很大的动态特性,下面我们将看到这种动态性。KVC是Objective C中一个非常基础,非常重要的机制,Cocoa绑定,Core Data,AppleScript等关键技术都是基于KVC实现的(还有KVO,键值观察)。
下面举个简单的例子说明KVC是如何使用的,假如有一个这样的类:
@interface Person : NSObject @property NSString* name; @property int* age; @end
可以这样使用KVC:
Person* p = [[Person alloc] init]; [p setValue:@"Lucy" forKey:@"name"]; [p setValue:@12 forKey:@"age"]; NSLog(@"%@'s age is %d!\n", [p valueForKey:@"name"], [p valueForKey:@"age"]);
输出的结果为:
2014-01-12 09:05:10.366 KVCExmples[1463:303] Lucy's age is 12!
使用了SetValue:forKey设置person的姓名和年龄,使用valueForKey获取Person的姓名和年龄。这个例子并没有直观的显示处KVC的威力,可以使用p.name=@“Lucy”; p.age=12; 来达到同样的目的。但是,当只有在运行时才能知道属性的名字的时候,KVC就派上用场了。比如,使用一个table显示一组Person的姓名和年龄,数据源应该是这样的:
-(id)tableView:(NSTableView*) tableView objectValueForTableColumn:(id)colum row:(NSInteger)row{ Person* p = [personArray objectAtIndex:row]; if([[colum identifier] isEqualToString:@"name"]) { return p.name; } else if([[colum identifier] isEqualToString:@"age"]) { return p.age; } return nil;}而使用KVC可以大大简化端代码:
Person* p = [personArray objectAtIndex:row]; return [p valueForKey:[colum identifier]];
初步了解了KVC的使用之后,下面将介绍KVC的基本用法。上边的例子中使用了-setValue:forKey:和valueForKey来获取和设置Person实例的name属性和age属性。为我们扩充一下上边的Person,增加了Person的department和creditCards属性。Person类的设计反映了这样一个模型,一个Person只能属于一个Department,一个Person可以有多个CreditCard。用术语来说,department是Person的一个to-one的属性,creditCards是一个to-many的属性。
@interface Department : NSObject @property NSString* name; @property NSString* decription; @end @interface CreditCard : NSObject @property NSString* cardNumber; @property int limit; @end @interface Person : NSObject @property NSString* name; @property int* age; @property Department* department; @property NSMutableArray* creditCards; @endKVC支持Key路径,通过使用 -setValue:forKeyPath:, -valueForKeyPath: 我们可以直接访问或者设置person所属的department的名称属性,路径的使用是这样的:
[p setValue:@"IT" forKeyPath:@"department.name"]; NSLog(@"my department name is %@\n", [p valueForKeyPath:@"department.name"]);
需要注意的是,如果路径中的某一层级不支持KVC,将抛出valueForUndefinedKey异常。关于支持KVC的条件,接下来将介绍。如果路径中某一层级本身是to-many关系,则返回一个数组。
最佳实践,除非确定Key的路径为一级的,否则应该使用-setValue:forPath和-valueForPath。
KVC还支持批量调用,使用-dictionaryWithValueForKeys: 和-setValueForKeyWithDictionary我们可以一次性设置或者取出多个属性值。看例子:
[p setValue:@"IT" forKeyPath:@"department.name"]; NSLog(@"my department name is %@\n", [p valueForKeyPath:@"department.name"]); NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: @"xiaohua", @"name", @15, @"age", nil]; [p setValuesForKeysWithDictionary:dict]; NSArray* array = [NSArray arrayWithObjects:@"name", @"age", nil]; NSDictionary* dictRet =[p dictionaryWithValuesForKeys:array]; NSLog(@"name is: %@, age is %@\n", [dictRet valueForKey:@"name"], [dictRet valueForKey:@"age"]);输出为:2014-01-12 15:16:16.763 KVCExmples[3349:303] name is: xiaohua, age is 15
对于to-many关系的creditCard,使用
[p setValue:cards forKey:@"creditCards"]; [p valueForKey:@"creditCards”];将creditCards看作一个to-one属性来使用。苹果文档上说,在to-many属性上使用-valueForKey返回的是一个不可变的集合,但是我测试可以将其转换为可变集合并修改的。不清楚怎么回事。
NSMutableArray* cs = [p valueForKey:@"creditCards"]; for (CreditCard* c in cs) { NSLog(@"number:%@, limit:%d", c.cardNumber, c.limit); } [cs addObject:card2]; NSMutableArray* cs2 = [p valueForKey:@"creditCards"]; for (CreditCard* c in cs2) { NSLog(@"number:%@, limit:%d", c.cardNumber, c.limit); }
结果为:
2014-01-13 08:17:17.507 KVCExmples[551:303] number:123, limit:10000
2014-01-13 08:17:17.508 KVCExmples[551:303] number:23456, limit:2134
2014-01-13 08:17:17.509 KVCExmples[551:303] number:123, limit:100002014-01-13 08:17:17.510 KVCExmples[551:303] number:23456, limit:2134
2014-01-13 08:17:17.510 KVCExmples[551:303] number:23456, limit:2134
可以看到确实得到了修改。不知道是怎么回事。
但是还是建议使用官方文档建议的-mutableArrayValueForKey,-mutableSetValueForKey,返回一个可变集合的代理。同样也有对应的-mutableArrayValueForKeyPath和-mutableSetValueForKeyPath.
KVC提供的方法是由NSKeyValueCoding协议指定的,NSObject遵守了这个协议,因此所有从NSObject中直接或者间接派生的类都已经遵守了这个协议。但是仅仅遵守协议是不够的,使用KVC还必须遵守一些约定,才能让类的一个属性支持KVC调用。如果调用传入了一个类不支持的Key,就会得到一个运行时错误:this class is not key value coding-compliant for the key XXX下面就介绍遵守KVC约定的类必须满足的条件。
对于to-one属性,KVC约定要求:
(1)实现-<Key>方法,如果是BOOL属性,-is<key>也可以; 或者,有成员变量叫做<Key>或者_<Key>;
(2)如果属性可读,需要提供-set<Key>方法
(3)如果属性支持验证,必须实现-validate<Key>:error:方法。
下面解释一下,在Person类中,有一个@property NSString* name;我们知道,@propery关键字自动帮我们实现了-setName和name方法,因此name属性是遵守KVC约定的(同理,如果实现了-setName和-name方法,我们也自动有了一个属性name)。如果Person没有一个@property叫做name,也可以通过下列方式满足KVC约定:
@implementation Person { NSString* _name; } @end
通过这种方式,可以在不暴露_name属性的前提下实现KVC。
这里有一个特殊的情况,age。KVC方法valueForKey,setValue:forKey中的value参数都是对象类型,而age的属性是值类型。不过不用担心,KVC会自动进行装箱和拆箱操作,特殊的是,如果给value传入nil,将会得到运行时异常:Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<Person 0x10010a390> setNilValueForKey]: could not set nil as the value for the key age.’ 为了避免这种异常,应该在Person中重写setNilValueForKey:方法。setNilValueForKey方法也是NSKeyValueCoding中的方法,在nil被当作参数的时候调用,默认的,如果给一个值属性传入一个nil,抛出NSInvalidArgumentException,我们可以重写这个方法,改变其行为,比如不抛出异常,而是给age赋值为0
- (void)setNilValueForKey:(NSString *)key { if([key isEqualToString:@"age"]) { [self setValue:[NSNumber numberWithFloat:0.0] forKey:@"age"]; } else { [super setNilValueForKey:key]; } }
对于to-many属性,分为有序和无序集合两种,对于有序集合,KVC约定要求:
(1)实现-<Key>方法,返回一个array。或者有一个成员变量叫做<Key>或者_<Key>.
(2)或者实现下列方法组合:-countOf<Key>,-objectIn<Key>AtIndex:,和-<Key>AtIndexes:后两者选一或者都实现。
(3)出于性能考虑,也可以实现-get<Key>:range:
如果属性是可变的,还应该满足下列条件:
(4)-insertObject:in<Key>AtIndex:, -insert<Key>:atIndex:,二者选一或者都实现。
(5)-removeObjectFrom<Key>AtIndex:, -remove<Key>AtIndex:,二者选一或者都实现。
(6)同样,出于性能考虑,应该实现-replaceObjectIn<Key>AtIndex:withObject:或者-replace<Key>AtIndex:with<Key>:.
对于无序集合,KVC要求:
(1)实现-<Key>方法,返回一个set。或者有一个成员变量叫做<Key>或者_<Key>
(2)或者实现方法组合:-countOf<key>:, -enumeratorOf<Key>:和memberOfKey:
如果属性是可变的,还应该满足下列条件:
(3)-add<Key>Object:,-add<Key>二者选一或者都实现。
(4)-remove<Key>Object, -remove<Key>二者选一,或者都实现。
(5)出于性能考虑,应该实现-intersect<Key>或者—set<Key>
0 0
- Coco学习之KVC
- OC学习之KVC
- OC之KVC、KVO 学习
- iOS学习之KVO、KVC
- OC学习篇之-----KVC
- Coco
- Coco
- OC学习总结之KVC KVO 通知
- iOS学习之KVC中setValuesForKeysWithDictionary:
- IOS学习笔记之KVC、KVO
- 学习笔记之KVC,KVO要点总结
- KVC学习
- OC学习篇之---KVC和KVO操作
- OC高级语法之KVC和KVO的学习
- OC学习篇之---KVC和KVO操作
- OC学习总结之KVC KVO 通知
- swift学习之--函数、构建函数、kvc构建函数
- ios学习--kvo/kvc
- c#数据采集(定向抓取)【待整理】
- 腾讯、网易、新浪新闻网站爬虫编写记录及评论格式分析
- 目前为止,你从你的老板或者上级那学到的最受用的一句话是什么?他(她)让你最佩服的品质又是什么?
- SiteMesh 3.0版本的简单使用
- iPad电子课本
- Coco学习之KVC
- java--分页查询接口设计及分页器的实现
- VMware Workstation 9安装MAC OS 10.8全程图解
- php生成带logo的二维码
- LINUX下动态链接库dlopen dlsym dlclose函数使用说明
- C++编程参考
- JDBC连接MySQL
- 又一年,写在新年里
- iOS开发-模式视图