刨根问底KVC
KVC 全称 key valued coding 键值编码
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性.JAVA,C#都有这个机制。ObjC也有,所以你根部不必进行任何操作就可以进行属性的动态读写,就是KVC。
KVC的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVC操作。
Person.h
1 2 3 4 5 6 7 8 9 10 11 12
|
@interface Person : NSObject { int weight; } @property(nonatomic,readonly,copy) NSString *name; @property(nonatomic,readonly, assign) int age; @property(nonatomic,strong) Dog * dog; @property(nonatomic,assign) id ID;
-(instancetype)initWithDict:(NSDictionary *)dict;
@end
|
Person.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
@implementation Person { int _height; }
-(instancetype)initWithDict:(NSDictionary *)dict { if (self=[super init]) {
|
main.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
Person * p1 = [[Person alloc]init]; Dog * d1 = [[Dog alloc] init]; p1.dog = d1;
|
KVC查找属性的顺序
- 用@property定义的属性的key值
- setter方法的key值
- 直接访问成员变量,先找key,如果找不到,再找_key
- 以上三种都未找到就会调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key 方法。
- 如果没有重写setValue:forUndefinedKey程序会马上崩溃。
注意:KVC可以访问成员变量,无论是否提供getter/setter方法,无论可见性是怎样,是否有readonly修饰。
setValue:forUndefinedKey与valueForUndefinedKey的应用
KVC的主要用途无非是ORM映射,就是将dictionary转换成model,但有些服务器返回的字段有可能是oc的关键字比如‘id’,’description’等。如上代码举得id的例子,我们无法让@property后面key值为id,于是使用大写的ID代替,KVC是区分大小写的我们不用担心。这时我们只需在setValue:forUndefinedKey把id的key值赋值给ID的key值,就可以避免关键字的尴尬。
KVC的逆向使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
Person * p1 = [[Person alloc]init]; [p1 setValue:@"170" forKey:@"height"]; [p1 setValue:@"70" forKey:@"weight"]; [p1 setValue:@"11111" forKey:@"id"];
NSArray * arr = @[@"height",@"weight",@"id"]; NSDictionary * dict = [p1 dictionaryWithValuesForKeys:arr]; NSLog(@"%@",dict);
log日志: { height = 170; id = 11111; weight = 70; }
|
NSArray/NSSet等都支持KVC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
Person * p1 = [[Person alloc]init]; Dog * d1 = [[Dog alloc] init]; d1.name = @"iPhone"; p1.dog = d1; Person * p2 = [[Person alloc]init]; Dog * d2 = [[Dog alloc] init]; d2.name = @"ios"; p2.dog = d2;
NSArray *[email protected][p1,p2]; NSArray *arrayM=[persons valueForKeyPath:@"dog.name"]; NSLog(@"%@",arrayM);
log日志: ( iPhone, ios )
|
KVC计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
Person * person = [[Person alloc] init];
NSMutableArray * books = [NSMutableArray array]; for (int i = 0; i < 10; i++) { Book * book = [[Book alloc] init]; book.price = i; [books addObject:book]; }
person.books = books;
NSNumber *bookCount = [person valueForKeyPath:@"[email protected]"]; NSLog(@"book count :%@", bookCount);
NSNumber *sum = [person valueForKeyPath:@"[email protected]"]; NSLog(@"sum :%@", sum);
NSNumber *avg = [person valueForKeyPath:@"[email protected]"]; NSLog(@"vag :%@", avg);
NSNumber *min = [person valueForKeyPath:@"[email protected]"]; NSLog(@"min :%@", min);
NSNumber *max = [person valueForKeyPath:@"[email protected]"]; NSLog(@"max :%@", max);
|
KVC底层实现
1 2 3 4 5 6 7 8 9
|
比如说如下的一行KVC的代码: [person setValue:@"dahuan" forKey:@"name"]; 就会被编译器处理成: SEL sel = sel_get_uid ("setValue:forKey:"); IMP method = objc_msg_lookup (person->isa,sel); method(person, sel, @"dahuan", @"name");
|
KVC与runtime应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
#import "MyModel.h" #import <objc/runtime.h> @implementation MyModel
|
最后附苹果KVC官方文档:
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/KeyValueCoding.html
另外.....
我的愿望是.......
世界和平.........