objective-c KVC机制

来源:互联网 发布:dota2 多核优化 编辑:程序博客网 时间:2024/05/17 03:51

Objective-C支持一种更灵活的操作方式,这种方式允许以字符串形式间接操作对象的属性,这种方式的全称是KeyValueCoding,即键值编码。


简单的KVC
最基本的KVC是由NSKeyValueCoding协议提供支持,最基本的操作属性的两个方法如下。
setValue:属性名:获取指定属性的值。
valueForKey:属性名:获取指定属性的值。


#import <Foundation/Foundation.h>
@interface FKUser:NSObject
@property(nonatomic, copy)NSString* name;
@property(nonatomic, copy)NSString* pass;
@property(nonatomic, copy)NSDate* birth;
@end


使用KVC来设置FKUser对象的属性,以及访问FKUser对象的属性。
#import "FKUser.h"
int main(int argc, char* argv[]){
@autoreleasepool{
FKUser* user = [[FKUser alloc]init];
[user setValue:@"孙悟空" forKey:@"name"];
[user setValue:@"1455" forKey:@"pass"];
[user setValue:[[NSDate alloc]init] forKey:@"birth"];
NSLog(@"user的name为:%@", [user valueForKey:@"name"]);
NSLog(@"user的pass为:%@", [user valueForKey:@"pass"]);
NSLog(@"user的birth为:%@", [user valueForKey:@"birth"]);
}
}


在KVC编程方式中,无论调用setValue:forKey方法,还是调用valueForKey:方法,都是通过NSString对象来指定被操作属性的,其中forKey:标签用于传入属性名。


对于setValue:属性值 forKey@"name"代码,代码通过setter方法完成设置。
1.程序优先考虑调用“setName:属性值”代码通过setter方法完成设置。
2.如果该类没有setName:方法,KVC机制会搜索该类名为_name的成员变量,无论该成员变量实在类接口部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际上就是对_name成员变量赋值。
3.如果该类既没有setName:方法,也没有定义_name成员变量,KVC机制会搜索该类名为name的成员变量,无论该成员是在类接口部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际就是对name成员变量赋值。
4.如果上面3条都没有找到,系统将会执行该对象的setValue:forUndefinedKey:方法。
默认的setValue:forUnderfinedKey:方法实现就是引发一个异常,这个异常将会导致程序因为异常结束。


对于“valueForKey@"name"”代码,底层的执行机制如下:
1.程序优先考虑调用"name",代码来获取该getter方法的返回值。
2.如果该类没有name方法,KVC机制会搜索该类名为_name的成员变量,无论该成员变量是在类接口部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际就是返回_name成员变量的值。
3.如果该类既没有name方法,也没有定义_name成员变量,KVC机制会搜索该类名为name的成员变量,无论该成员变量是在类接口部分定义,还是在类实现部分定义,也无论用哪个访问控制符修饰,这条KVC代码底层实际就是返回name成员变量的值。
4.如果上面3条都没有找到,系统将会执行该对象的valueforUndefinedKey:方法。
默认的valueforUndefinedKey:方法实现就是引发一个异常,这个异常将会导致程序因为异常结束。


处理不存在的key
当使用KVC方式操作属性时,这些属性可能并不存在——既不存在对应的setter和getter方法,也不存在对应的成员变量,KVC将会自动调用setValue:forUndefinedKey:和valueForUnderfinedKey:方法——但系统默认实现的这两个方法仅仅只是引发异常,并没有进行任何特别的处理。
只要在类的实现部分重写setValue:forUnderfinedKey:方法,甚至不需要在类的接口声明该方法。
Objective-C并不存在绝对隐藏的方法,即使一个方法仅仅在类实现部分定义,根本不放在类接口部分定义,程序依然可通过NSObject提供的performSelector:或performSelector:withObject:方法调用到Objective-C对象的方法。
KVC机制将会调用valueForUndefinedKey:方法,为了实现自定义行为,考虑在类实现部分重写valueForUndefinedKey:(id)key。
此时会看到,当KVC操作并不存在的key时,KVC机制总是调用我们重写过的方法进行处理,通过这种处理机制,可以非常方便地定制自己的处理行为。


处理nil值
当调用KVC来设置对象的属性时,如果属性的类型是基本类型(如int、float、double)且程序传入了对应类型的值,那么程序当前可以正确地进行设置。但如果我们尝试为基本类型的属性设置一个nil,会导致什么结果呢?KVC会把nil当成0,还是1?
#import <Foundation/Foundation.h>
@interface FKItem : NSObject
@property(nonatomic, copy)NSString* name;
@property(nonatomic, assign)int price;
@end
上面的程序中,尝试将NSString类型的name属性设置为nil,这是合法的,完全可以正常执行,但尝试将int类型的price设置为nil时,程序就有可能引发错误。
上面这段提示信息实际是由程序中的setNilValueForKey:方法所产生的。也就是说,当程序尝试为某个属性设置nil值时,如果该属性并不接受nil值,那么程序将会自动执行该对象的setNilValueForKey:方法。如果为了自行定制这个方法,同样可通过重写setNilValueForKey:方法来实现。
-(void)setNilValueForKey:(id)key
{
//如果尝试将key为price的属性设为nil
if([key isEqualToString:@"price"]){
//将该price设置为0
price = 0;
}else{
[super setNilValueForKey:key];
}
}




Key路径
kVC除了操作对象的属性之外,还可操作对象的"复合属性"。所谓“复合属性”,KVC机制将其称为Key路径,比如,FKOrder对象内包含一个FKItem类型的item属性,而FKItem对象又包含了name属性和price属性,那么KVC可以通过item.name、item.price这种Key路径来支持操作FKOrder对象的name、price属性。
KVC协议中为操作Key路径的方法如下:
setValue:forKeyPath:根据Key路径设置属性值。
valueForKeyPath:根据key路径获取属性值。
如下程序定义了一个FKOrder(订单)类,该FKOrder类中包含了一个FKItem类型的属性:
#import <Foundation/Foundation.h>
#import "FKItem.h"
@interface FKOrder:NSObject
@property(nonatomic, strong)FKItem* item;
@property(nonatomic, assign)int amount;
-(int) totalPrice;
@end
#import "FKOrder.h"
int main(int argc,char * argv[]){
@autoreleasepool{
FKOrder *order = [[FKOrder alloc]init];
[order setValue:@"12" forKey:@"amount"];
[order setValue:[[FKItem alloc] init] forKey:@"item"];
[order setValue:@"鼠标" forKeyPath:@"item.name"];
[order setValue:[NSNumber numberWIthInt:20] forKeyPath:@"item.price"];
NSLog(@"订单包含%@个%@,总价为:%@", [order valueForKey:@"amount"], [order valueForKeyPath:@"item.name"],
[order valueForKey:@"totalPrice"]);
}
}
0 0
原创粉丝点击