刨根问底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]) { //字典转模型的常用语句 [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
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;
//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的逆向使用
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 *persons=@[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:@"books.@count"]; NSLog(@"book count :%@", bookCount);
NSNumber *sum = [person valueForKeyPath:@"books.@sum.price"]; NSLog(@"sum :%@", sum);
NSNumber *avg = [person valueForKeyPath:@"books.@avg.price"]; NSLog(@"vag :%@", avg);
NSNumber *min = [person valueForKeyPath:@"books.@min.price"]; NSLog(@"min :%@", min);
NSNumber *max = [person valueForKeyPath:@"books.@max.price"]; 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
//解档 - (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官方文档:
https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/KeyValueCoding.html
另外.....
我的愿望是.......
世界和平.........
- IOS设计模式之一(MVC模式,单例模式)
iOS 设计模式-你可能已经听说过这个词,但是你真正理解它意味着什么吗?虽然大多数的开发者可能都会认为设计模式是非常重要的,然而关于设计模式这一主题的文章却不多,并且有时候我们开发者在写代码的时候也不 ...
- iOS 设计模式之工厂模式
iOS 设计模式之工厂模式 分类: 设计模式2014-02-10 18:05 11020人阅读 评论(2) 收藏 举报 ios设计模式 工厂模式我的理解是:他就是为了创建对象的 创建对象的时候,我们一 ...
- iOS设计模式之生成器
iOS设计模式之生成器 1.生成器模式的定义 (1): 将一个复杂的对象的构件与它的表示分离,使得相同的构建过程能够创建不同的表示 (2): 生成器模式除了客户之外还包括一个Director(指导者) ...
- iOS:KVO/KVC 的概述与使用
iOS:KVO/KVC 的概述与使用 KVO APP开发技术QQ群:347072638 一,概述 KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性 ...
- IOS设计模式之三:MVC模式
IOS设计模式之三:MVC模式 模型-视图-控制器 这个模式其实应该叫做MCV,用控制器把model与view隔开才对,也就是model与view互相不知道对方的存在,没有任何瓜葛,他们就像一个团 ...
- iOS设计模式 - 享元
iOS设计模式 - 享元 原理图 说明 享元模式使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件.通常物件中的部分 ...
- iOS设计模式 - 责任链
iOS设计模式 - 责任链 原理图 说明 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...
- iOS设计模式 - 模板
iOS设计模式 - 模板 原理图 说明 定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. 源码 https://github.c ...
- iOS设计模式 - 访问者
iOS设计模式 - 访问者 原理图 说明 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 1.Visitor 抽象访问者角色,为该对象结构中具 ...
随机推荐
- devstack环境搭建
1. devstack部署 参考Quick Start,推荐使用ubuntu16.04进行安装 1.1 配置ubuntu国内源 修改/etc/apt/sources.list内容为 deb http: ...
- Leetcode 3. Longest Substring Without Repeating Characters (Medium)
Description Given a string, find the length of the longest substring without repeating characters. E ...
- BZOJ 4012 HNOI2015 开店 树的边分治+分治树
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4012 题意概述:给出一颗N点的树,保证树上所有点的度不超过3,树上每个点有权值,每条边有权 ...
- 【转】H5 - HTML5新增标签
下面分别是传统的div+css的页面布局方式 下面是HTML5布局方式: 是不是精简了很多呢 现在来说说图片中出现的标签: 结构标签:(块状元素) 有意义的div artical 标记定义一篇文章 ...
- beta版本冲刺四
目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员:恺琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示组 ...
- 团队作业4——第一次项目冲刺(Alpha版本)-第一篇
第一次项目冲刺——第一阶段 今天我们在宿舍开了个会,每个人都斗志昂扬的.撸起袖子加油干! 分工讨论 团队成员 任务 郭达 完成博客随笔和leangoo 刘德培 设计好数据库 石浩洋 搭建好LAM ...
- 如何创建LocalDB数据库和数据库实例
LocalDB是SQL Server 2012带来的新特性,它是一个专门为开发人员量身定制的轻量级数据库,下面介绍如何使用它. 创建LocalDB数据库的方法: 打开服务器资源管理器,右键点击“数据连 ...
- 解决Linux下启动Tomcat遇到Neither the JAVA_HOME ...报错
解决Linux下启动Tomcat遇到Neither the JAVA_HOME ...报错 Neither the JAVA_HOME nor the JRE_HOME environment var ...
- php开发中处理emoji表情和颜文字的兼容问题
背景:随着手机的普及,现在移动开发很火爆,已经远远超过了pc端.在移动设备经常会发生用户发送的内容中包含emoji表情,在显示时就是乱码.一般是因为Mysql表设计时,都是用UTF8字符集的.把带有e ...
- spring笔记(二)
共性问题: 1. 服务器启动报错,什么原因? * jar包缺少.jar包冲突 1) 先检查项目中是否缺少jar包引用 2) 服务器: 检查jar包有没有发布到服务器下: 用户库jar包,需要手动发布到 ...