刨根问底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查找属性的顺序

  1. 用@property定义的属性的key值
  2. setter方法的key值
  3. 直接访问成员变量,先找key,如果找不到,再找_key
  4. 以上三种都未找到就会调用- (void)setValue:(id)value forUndefinedKey:(NSString *)key 方法。
  5. 如果没有重写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设计模式 —— KVC的更多相关文章

  1. IOS设计模式之一(MVC模式,单例模式)

    iOS 设计模式-你可能已经听说过这个词,但是你真正理解它意味着什么吗?虽然大多数的开发者可能都会认为设计模式是非常重要的,然而关于设计模式这一主题的文章却不多,并且有时候我们开发者在写代码的时候也不 ...

  2. iOS 设计模式之工厂模式

    iOS 设计模式之工厂模式 分类: 设计模式2014-02-10 18:05 11020人阅读 评论(2) 收藏 举报 ios设计模式 工厂模式我的理解是:他就是为了创建对象的 创建对象的时候,我们一 ...

  3. iOS设计模式之生成器

    iOS设计模式之生成器 1.生成器模式的定义 (1): 将一个复杂的对象的构件与它的表示分离,使得相同的构建过程能够创建不同的表示 (2): 生成器模式除了客户之外还包括一个Director(指导者) ...

  4. iOS:KVO/KVC 的概述与使用

    iOS:KVO/KVC 的概述与使用       KVO   APP开发技术QQ群:347072638 一,概述 KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性 ...

  5. IOS设计模式之三:MVC模式

    IOS设计模式之三:MVC模式   模型-视图-控制器 这个模式其实应该叫做MCV,用控制器把model与view隔开才对,也就是model与view互相不知道对方的存在,没有任何瓜葛,他们就像一个团 ...

  6. iOS设计模式 - 享元

    iOS设计模式 - 享元 原理图 说明 享元模式使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件.通常物件中的部分 ...

  7. iOS设计模式 - 责任链

    iOS设计模式 - 责任链 原理图 说明 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链 ...

  8. iOS设计模式 - 模板

    iOS设计模式 - 模板 原理图 说明 定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. 源码 https://github.c ...

  9. iOS设计模式 - 访问者

    iOS设计模式 - 访问者 原理图 说明 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 1.Visitor 抽象访问者角色,为该对象结构中具 ...

随机推荐

  1. LeetCode 876——链表的中间结点

    1. 题目 给定一个带有头结点 head 的非空单链表,返回链表的中间结点. 如果有两个中间结点,则返回第二个中间结点. 示例 1: 输入:[1,2,3,4,5] 输出:此列表中的结点 3 (序列化形 ...

  2. Java 访问权限控制 小结

    总所周知,Java提供了访问权限修饰词,以供类库开发人员向客户端程序员指明哪些是可用的,哪些是不可用的. 访问权限控制的等级,从最大权限到最小权限依次为:public.protected.包访问权限( ...

  3. 关于ubuntu上无法运行cmd markdown

    环境:ubuntu18.04 直接解压完的cmd markdown,直接点击可执行文件运行,没有反应,在通过终端运行,提示:Cannot find required executable ifconf ...

  4. 软工实践 - 第二十八次作业 Beta 冲刺(6/7)

    队名:起床一起肝活队 组长博客:https://www.cnblogs.com/dawnduck/p/10146478.html 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过 ...

  5. JVM(2)——GC算法和收集器

    一.引入 上篇博客<JVM--简介>中主要介绍了JVM的内存模型,思考一下: 为什么要划分堆.栈.方法区等? 为什么把不同种类的数据信息分别存放? 答案可以分为很多很多条,这里就说一个方面 ...

  6. 【EasyNetQ】- 快速开始

    欢迎来到EasyNetQ.本指南向您展示如何在大约10分钟内启动并运行EasyNetQ. 你可以在GitHub上找到这个快速入门的代码:https://github.com/mikehadlow/Ea ...

  7. RxAndroid+RxJava+Gson+retrofit+okhttp初步搭建android网络请求框架

    新建工程集成, 一.工具集成(2017-4-27) 首先第一步集成retrofit retrofit 的 git 网站: https://github.com/square/retrofit 在git ...

  8. JQuery排错关于$(document).ready(function(){});

    最近写了好多JQuery.也出了很多问题.不知道怎么回事.程序就不往下执行了.很是郁闷. 查了下资料,这里可能会有以下几种原因:1.js文件的引用路径不正确,特别是使用了命名空间,容易造成路径错误,使 ...

  9. Codeforces Round #392(Div 2) 758F(数论)

    题目大意 求从l到r的整数中长度为n的等比数列个数,公比可以为分数 首先n=1的时候,直接输出r-l+1即可 n=2的时候,就是C(n, 2)*2 考虑n>2的情况 不妨设公比为p/q(p和q互 ...

  10. npm错误总结

    You cannot publish over the previously published version 1.0.1." : xxx 发布时一定要修改package.json的版本号 ...