刨根问底KVC
KVC 全称 key valued coding 键值编码
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性.JAVA,C#都有这个机制。ObjC也有,所以你根部不必进行任何操作就可以进行属性的动态读写,就是KVC。
KVC的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVC操作。
Person.h
123456789101112 |
@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
1234567891011121314151617181920212223242526272829303132333435 |
@implementation Person{ int _height;} -(instancetype)initWithDict:(NSDictionary *)dict{ if (self=[super init]) { //字典转模型的常用语句 [self setValuesForKeysWithDictionary:dict]; } return self;} //当key的值是没有定义时,设置会执行的方法- (void)setValue:(id)value forUndefinedKey:(NSString *)key { if ([key isEqualToString:@"id"]) { self.ID = value; }} //当key的值是没有定义时,取值时执行的方法- (id)valueForUndefinedKey:(NSString *)key { if ([key isEqualToString:@"id"]) { return self.ID; } return [NSNull null];} |
main.m
1234567891011121314151617181920212223242526272829303132 |
Person * p1 = [[Person alloc]init];Dog * d1 = [[Dog alloc] init];p1.dog = d1; //setValue:属性值 forKey:属性名(用于简单路径) //使用KVC间接修改对象属性时,系统会自动判断对象属性的类型,并完成转换。如该程序中的“170”. [p1 setValue:@"170" forKey:@"height"];[p1 setValue:@"70" forKey:@"weight"];[p1 setValue:@"1" forKey:@"id"]; //setValue:属性值 forKeyPath:属性路径(用于复合路径)//用KVC取一个嵌套层次很深的路径的时候,只要给它一个路径就能把想要的属性给拿出来。(.可以理解为路径。一直一直进入)。能够帮助我们很方便的编码。[p1 setValue:@"dahuan" forKeyPath:@"dog.name"]; //valueForKey:属性名 、valueForKeyPath:属性名(用于复合路径)NSLog(@"height = %d weight = %d id = %@ dog.name = %@" ,[[p1 valueForKey:@"height"] intValue],[[p1 valueForKey:@"weight"] intValue],[p1 valueForKey:@"id"],[p1 valueForKeyPath:@"dog.name"]); log日志:height = 170 weight = 70 id = 11111 dog.name = dahuan所有的属性都可以赋值成功 NSDictionary * dict = @{@"height":@"160", @"weight":@"60", @"id":@"11101" }; Person * p2 = [[Person alloc] initWithDict:dict]; NSLog(@"height = %d weight = %d id = %@",[[p2 valueForKey:@"height"] intValue],[[p2 valueForKey:@"weight"] intValue],[p2 valueForKey:@"id"]); log日志:height = 160 weight = 60 id = 11101所有的属性都可以赋值成功 |
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的逆向使用
12345678910111213141516 |
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
1234567891011121314151617181920 |
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计算
12345678910111213141516171819202122232425 |
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底层实现
123456789 |
比如说如下的一行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应用
123456789101112131415161718192021222324252627282930313233343536373839404142 |
#import "MyModel.h"#import <objc/runtime.h> @implementation MyModel //解档- (id)initWithCoder:(NSCoder *)decoder{ if (self = [super init]) { unsigned int count = 0; //获取类中所有成员变量名 Ivar *ivar = class_copyIvarList([MyModel class], &count); for (int i = 0; i<count; i++) { Ivar iva = ivar[i]; const char *name = ivar_getName(iva); NSString *strName = [NSString stringWithUTF8String:name]; //进行解档取值 id value = [decoder decodeObjectForKey:strName]; //利用KVC对属性赋值 [self setValue:value forKey:strName]; } free(ivar); } return self;} //归档- (void)encodeWithCoder:(NSCoder *)encoder{ unsigned int count; Ivar *ivar = class_copyIvarList([MyModel class], &count); for (int i=0; i<count; i++) { Ivar iv = ivar[i]; const char *name = ivar_getName(iv); NSString *strName = [NSString stringWithUTF8String:name]; //利用KVC取值 id value = [self valueForKey:strName]; [encoder encodeObject:value forKey:strName]; } free(ivar);}@end |
最后附苹果KVC官方文档:
另外.....
我的愿望是.......
世界和平.........
时间: 2024-10-05 10:20:25