OC中我们通常可以通过getter 和setter 来获得和修改到对象属性的值,但是还有一种更加灵活的操作方式,KVC(key value coding);
直接上代码吧
首先我们先看一个Novel类:
接口部分
#import
//小说
@interface Novel : NSObject
{
@private
NSString* _name; //小说的名字
int _price; //小说的价格
}
//为_name _price添加getter setter方法
@property (nonatomic,copy,getter=getName) NSString* name;
@property (nonatomic,getter=getPrice) int price;
//打印小说信息
-(void) showInfo;
@end
实现部分
#import "Novel.h"
@implementation Novel
@synthesize name = _name;
@synthesize price = _price;
-(void)showInfo{
NSLog(@"小说的名字:【%@】, 价格:【%d】",_name,_price);
}
@end
当我们直接使用getter 或者 setter 方法对属性操作的时候,如下:
//首先创建一个Novel 对象
Novel* aNovel = [[Novel alloc]init];
// 为 aNovel设置属性
[aNovel setName:@"鬼吹灯之九层妖塔"];
[aNovel setPrice:48];
NSLog(@"小说:【%@】,价格【%d】",[aNovel getName],[aNovel getPrice]);
现在我们来介绍一下KVC方式
[aNovel setValue:@"鬼吹灯之寻龙诀" forKey:@"name"];
[aNovel setValue:[NSNumber numberWithInt:58] forKey:@"price"];
NSLog(@"小说:【%@】,价格【%@】",[aNovel valueForKey:@"name"], [aNovel valueForKey:@"price"]);Value:[需要设置的值] forKey:[所要操作的属性];
在这里说明一下底层执行机制,以name为例子
如果以上3条都没有找到,系统将会执行该对象的setValue:forUnderfinedKey方法。
下面我们来测试一下上述的2、3条
我们在Novel 中 添加两个属性,test _test
@private
NSString* _name; //小说的名字
int _price; //小说的价格
@package
// 添加两个test属性
NSString* test;
NSString* _test;
现在我们在main函数的时候,用KVC来查找
[aNovel setValue:@”value” forKey:@”test”];
NSLog(@”打印test属性:%@”,aNovel->test);
NSLog(@”打印test属性:%@”,aNovel->_test);
打印结果
打印test属性:(null)
打印test属性:value
从这里可以看出来KVC的搜索顺序为:
①setTest:方法 ②_name成员变量 ③name成员变量
这里我们把打印修改一下
NSLog(@"打印test属性:%@",[aNovel valueForKey:@"test"]);
NSLog(@"打印_test属性:%@",[aNovel valueForKey:@"_test"]);
大家猜猜输出结果。
没错 结果就是:
打印test属性:value
打印_test属性:value
分析一下
首先看看第一个 valueForKey:@”test”,因为test属性和_test属性都没有setter方法。按顺序KVC开始搜索_test属性,查找到_test的值是value。
然后我们看看第二个 valueForKey:@”_test”, 同样没有setter方法。按顺序搜索 __test属性,没有,再查找 _test属性。得到值value。
直接看看代码
main函数
[aNovel setValue:@"不存在的key" forKey:@"noKey"];
看看打印结果
Terminating app due to uncaught exception ‘NSUnknownKeyException‘, reason: ‘[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key noKey.’
程序中断 并且打印出上面的信息了。为了自行处理这个异常,我们可以在类的实现部分重写setValue:forUndefinedKey方法
//重写setValue:forUndefinedKey
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@"你尝试设置的 key :【%@】并不存在!",key);
NSLog(@"你设置的value :【%@】 ",value);
}
同样当我们通过valueForKey 方法得到相应key的值,如果key不存在也会抛出异常的,我们可以重写valueForUndefinedKey
//重写valueForUndefinedKey
-(id)valueForUndefinedKey:(NSString*)key{
NSLog(@"你设置的key :【%@】 ",key);
return nil;
}
我们来看看nil值,如果我们通过KVC为一个存在的key赋值的时候,如果是基本类型(int 、float 、double等),如果传nil的话会不会抛出异常呢?来看看吧
// 在Novel.h中添加 _numb属性
int _numb;
// 在main函数中测试
[aNovel setValue:nil forKey:@"_numb"];
看看打印结果
//抛出异常 并且 程序终端
Terminating app due to uncaught exception ‘NSInvalidArgumentException‘, reason: ‘[ setNilValueForKey]: could not set nil as the value for the key _numb.’
同样我们可以重写setNilValueForKey:
-(void)setNilValueForKey:(NSString *)key{
if([key isEqualToString:@"_numb"]){
//将该price设置成0
_numb = 0;
}else{
//回调父类的setNilValueForKey,默认执行
[super setNilValueForKey:key];
}
}
KVC协议中为操作Key路径的方法如下:
来直接看代码
先创建一个person类
#import
@interface Person : NSObject
{
//定义两个成员变量
@public
NSString* _name;
int _age;
}
// 打印名字 和 年龄
-(void) printNameAndAge;
//定义一个say方法,并不提供实现
-(void) say:(NSString*)content;
Novel类中添加一个author属性
Person* _author; //小说作者
在main函数中
[aNovel setValue:[[Person alloc]init] forKey:@"author"];
[aNovel setValue:@"天下霸唱" forKeyPath:@"author.name"];
[aNovel setValue:[NSNumber numberWithInt:43] forKeyPath:@"author.age"];
NSLog(@"作者:【%@】,年龄:【%@】",[aNovel valueForKeyPath:@"author.name"],[aNovel valueForKeyPath:@"author.age"]);
大致的KVC的基本用法就是这些了。