我之前写过用点语法来设置,修改对象的属性,其实也是通过setter,getter的方法来设置和修改对象的属性。实际上,OC还支持一种更加灵活的操作方式,这种方式允许以字符串的形式间接操作对象的属性。这种方式就是今天我要介绍的KVC.
一、简单的KVC
最简单的KVC的操作属性有两个方法如下:
> setValue:属性值forKey:属性名:为指定属性设置值。
>valueForKey:属性名:获取指定属性的值
例如一下例子:
程序代码:Dog.h
#import <Foundation/Foundation.h> @interface Dog : NSObject //使用@property定义3个property(属性) @property(nonatomic,copy)NSString * name; @property(nonatomic,copy)NSString * pass; @property(nonatomic,copy)NSString * birth; @end
程序代码:main.m
#import <Foundation/Foundation.h> #import "Dog.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建Dog对象 Dog *dog = [[Dog alloc]init]; //使用KVC方式为name设置属性值 [dog setValue:@"黑马哥" forKey:@"name"]; //使用KVC方式为pass设置属性值 [dog setValue:@"222" forKey:@"pass"]; //使用KVC方式为birth设置属性值 [dog setValue:@"sdfs" forKey:@"birth"]; //使用KVC获取dog对象的属性 NSLog(@"dog的name是:%@",[dog valueForKey:@"name"]); NSLog(@"dog的pass是:%@",[dog valueForKey:@"pass"]); NSLog(@"dog的birth是:%@",[dog valueForKey:@"birth"]); } return 0; }
<pre name="code" class="objc">这是使用KVC设置属性的值
[dog setValue:@"黑马哥" forKey:@"name"];
这是使用KVC获取指定属性的值。
<pre name="code" class="objc">NSLog(@"dog的name是:%@",[dog valueForKey:@"name"]);
2016-02-10 14:18:53.392 点语法访问属性[944:42376] dog的name是:黑马哥 2016-02-10 14:18:53.393 点语法访问属性[944:42376] dog的pass是:222 2016-02-10 14:18:53.393 点语法访问属性[944:42376] dog的birth是:sdfs Program ended with exit code: 0
对于setValue:属性值 forKey@“name”;代码,底层的执行机制如下:
#import <Foundation/Foundation.h> @interface Dog : NSObject { @package NSString * name; NSString *_name; } @end程序代码 Dog.m
#import "Dog.h" @implementation Dog { int age; } @end程序代码 main.m
#import <Foundation/Foundation.h> #import "Dog.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建Dog对象 Dog *dog =[[Dog alloc]init]; //使用KVC方式为name属性赋值,KVC搜索顺序为: //1、setName:方法;2、_name成员变量;3、name成员变量 [dog setValue:@"小狗" forKey:@"name"]; //访问name/_name两个成员变量的值 NSLog(@"dog->name:%@",dog->name); NSLog(@"dog->_name:%@",dog->_name); //使用KVC对age赋值,将会导致类实现部分定义的age成员变量被赋值 [dog setValue:[NSNumber numberWithInt:5] forKey:@"age"]; NSLog(@"dog的age:%@",[dog valueForKey:@"age"]); } return 0; }
2016-02-10 16:00:21.689 点语法访问属性[1021:57028] dog->name:(null) 2016-02-10 16:00:21.690 点语法访问属性[1021:57028] dog->_name:小狗 2016-02-10 16:00:21.691 点语法访问属性[1021:57028] dog的age:5 Program ended with exit code: 0
//1、setName:方法;2、_name成员变量;3、name成员变量当设置name是,执行到_name时,直接识别并输出,但是name并没有输出值,输出的为null。
#import <Foundation/Foundation.h> @interface Dog : NSObject @end
#import <Foundation/Foundation.h> #import "Dog.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建Dog对象 Dog *dog =[[Dog alloc]init]; [dog setValue:@"小狗" forKey:@"name"]; NSLog(@"dog的age:%@",[dog valueForKey:@"name"]); } return 0; }
2016-02-10 16:12:58.446 点语法访问属性[1049:59283] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Dog 0x100206710> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name.'上面异常的信息为:程序尝试设置的name并不存在,因此,程序引发了NSUnknownKeyException异常,我们在Dog.m文件中重写方法:
-(void)setValue:(id)value forUndefinedKey:(NSString *)key { NSLog(@"您设置的key:%@并不存在",key); NSLog(@"您设置的value为:%@",value); }
2016-02-10 18:06:15.387 点语法访问属性[1179:72930] 您设置的key:name并不存在 2016-02-10 18:06:15.388 点语法访问属性[1179:72930] 您设置的value为:小狗 2016-02-10 18:06:15.389 点语法访问属性[1179:72930] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Dog 0x100114550> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.'
-(id)valueForUndefinedKey:(NSString *)key { NSLog(@"您尝试访问的key:%@并不存在",key); return nil; }
#import <Foundation/Foundation.h> @interface Dog : NSObject //使用@property定义两个属性 @property(nonatomic,copy)NSString *name; @property(nonatomic,assign)int price; @end
#import <Foundation/Foundation.h> #import "Dog.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建Dog对象 Dog *dog =[[Dog alloc]init]; //使用KVC方式尝试为name,price设置属性为nil [dog setValue:nil forKey:@"name"]; [dog setValue:nil forKey:@"price"]; NSLog(@"dog的name为%@",[dog valueForKey:@"name"]); NSLog(@"dog的price为%@",[dog valueForKey:@"price"]); } return 0; }
2016-02-10 18:28:50.763 点语法访问属性[1294:79054] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<Dog 0x100203d40> setNilValueForKey]: could not set nil as the value for the key price.'
NSInvalidArgumentException
从上方程序可以看出,程序引发了一个‘NSInvalidArgumentException’异常,这就是由于int类型的属性不能接受nil所导致的,上面这段提示信息其实是有程序中的setNilValueForKey:方法所产生的。也就是说,当程序尝试为某个属性设置nil时,如果该属性并不接受nil值,那么程序将会自动执行该对象的setNilValueForKey:方法。如果为了定制这个行为,我们可以重写这个方法。在Dog的类的实现部分增加以下代码就可以-(void)setNilValueForKey:(NSString *)key { //如果尝试将key为price的属性设为nil if([key isEqualToString:@"price"]) { _price = 0; } else { //回调父类的setNilValueForKey,执行默认行为 [super setNilValueForKey:key]; } }
2016-02-10 18:37:20.098 点语法访问属性[1328:81680] dog的name为(null) 2016-02-10 18:37:20.100 点语法访问属性[1328:81680] dog的price为0
#import <Foundation/Foundation.h> @interface Dog : NSObject //使用@property定义两个属性 @property(nonatomic,copy)NSString *name; @property(nonatomic,assign)int price; @end
// // Item.h // Created by lyx on 16/2/10. // Copyright (c) 2016年 李云祥. All rights reserved. // #import <Foundation/Foundation.h> #import "Dog.h" @interface Item : NSObject @property(nonatomic,strong)Dog *dog; @property(nonatomic,assign)int amount; @property(nonatomic,assign)int aollPrice; @end
#import <Foundation/Foundation.h> #import "Item.h" int main(int argc, const char * argv[]) { @autoreleasepool { //创建Item对象 Item *item = [[Item alloc]init]; //使用KVC方式为amount赋值 [item setValue:@"287" forKey:@"amount"]; [item setValue:[[Dog alloc]init] forKey:@"dog"]; //使用setValue:forKeyPath设置dog属性的name属性 [item setValue:@"键盘" forKeyPath:@"dog.name"]; [item setValue:@"1189" forKeyPath:@"dog.price"]; NSLog(@"%@个%@,总的为:%@",[item valueForKey:@"amount"],[item valueForKeyPath:@"dog.name"],[item valueForKey:@"aollPrice"]); } return 0; }
2016-02-10 19:42:58.143 点语法访问属性[1508:93608] 287个键盘,总的为:0 Program ended with exit code: 0