iOS开发中KVC、KVO简介
在iOS开发中,KVC和KVO是经常被用到的。可以使用KVC对对象的属性赋值和取得对象的属性值,可以使用KVO监听对象属性值的变化。简单介绍一下KVC和KVO。
一:键值编码(KVC)
KVC,全称 Key Value Coding(键值编码),是OC 语言的一个特性,使用KVC,可以对对象的属性进行动态读写。
KVC的操作方法由 NSKeyValueCoding协议提供,而NSObject已经实现了这个协议,因此OC中的几乎所有对象都可以使用KVC操作。常用的KVC操作方法有:
(1)设置属性值
setValue:value forKey:key (用于简单路径,也就是直接属性)
setValue: value forKeyPath:key (用于复杂路径,也就是间接属性)
(2)获取属性值
valueForKey: key (用于获取简单路径的属性值)
valueForKeyPath: key (用于获取复杂路径的属性值)
通过代码来看KVC的具体使用:
两个类分别是Dog类和Person类
Dog.h

1 #import <Foundation/Foundation.h>
2
3 @interface Dog : NSObject
4
5 @property (nonatomic, copy) NSString *name;
6
7 @end

Dog.m
1 #import "Dog.h"
2
3 @implementation Dog
4
5 @end
Person.h

1 #import <Foundation/Foundation.h>
2
3 @class Dog;
4
5 @interface Person : NSObject
6
7 @property (nonatomic, copy) NSString *name;
8 @property (nonatomic, copy) NSString *sex;
9
10 @property (nonatomic, strong) Dog *dog;
11
12 @end

Person.m
1 #import "Person.h"
2
3 @implementation Person
4
5 @end
KVC的使用:

1 Person *person = [[Person alloc] init];
2 //kvc 设置值
3 [person setValue:@"Jim" forKey:@"name"];
4 [person setValue:@"boy" forKey:@"sex"];
5
6 //kvc 获得值
7 NSLog(@"name = %@ sex = %@",[person valueForKey:@"name"],[person valueForKey:@"sex"]);
8
9 Dog *dog = [[Dog alloc] init];
10 person.dog = dog;
11 //kvc 设置复杂路径值
12 [person setValue:@"Teddy" forKeyPath:@"dog.name"];
13 //kvc 获得复杂路径值
14 NSLog(@"dog name = %@",[person valueForKeyPath:@"dog.name"]);

可以看到,KVC使用对应的函数即可设置值、获得值。那么,KVC是如何查找一个属性进行读取呢?KVC遵循下面的规则,假设要对name属性进行读取:
(1)设置属性:优先考虑setName方法;如果没有,则搜索成员变量_name;如果没找到,则搜索成员变量name;如果还没有找到,则调用 setValue: forUndefineKey: 方法。
(2)读取属性:优先考虑getName方法;如果没有,则搜索成员变量_name;如果没找到,则搜索成员变量name;如果还没有找到,则调用 valueForUndefineKey: 方法。
二:KVC中 setValuesForKeysWithDictionary: 的使用
KVC中有一个非常重要的方法: setValuesForKeysWithDictionary:dict ,该方法可以将一个字典映射到一个对象,省去了给对象一一赋值的步骤。
使用 setValuesForKeysWithDictionary:dict 的一个例子:
student.h

1 #import <Foundation/Foundation.h>
2
3 @interface Student : NSObject
4
5 /**
6 * 学号
7 */
8 @property (nonatomic, copy) NSString *num;
9 /**
10 * 姓名
11 */
12 @property (nonatomic, copy) NSString *name;
13 /**
14 * 身高
15 */
16 @property (nonatomic, assign) float height;
17
18 /**
19 * 初始化的两个方法
20 *
21 */
22 - (instancetype)initWithDict:(NSDictionary *)dict;
23 + (instancetype)stuWithDict:(NSDictionary *)dict;
24
25
27
28 @end

student.m

1 - (instancetype)initWithDict:(NSDictionary *)dict{
2 if(self = [super init]){
3 [self setValuesForKeysWithDictionary:dict];
4 }
5 return self;
6 }
7
8 + (instancetype)stuWithDict:(NSDictionary *)dict
9 {
10 return [[self alloc] initWithDict:dict];
11 }
12

用一个字典初始化对象

1 - (void)initStudent
2 {
3 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
4 @"Tom",@"name",
5 @"110",@"num",
6 @"170.0",@"height",
7 nil];
8 Student *stu = [[Student alloc] initWithDict:dict];
9 NSLog(@"name = %@ num = %@ height = %f",stu.name,stu.num,stu.height);
10 }

setValuesForKeyWithDictionary:dict 的原理
实际上,setValuesForKeyWithDictionary:dict 方法就是遍历dict,对dict 中的每个键值调用 setValue: forKey: 方法。可以用下面的方法模拟setValuesForKeyWithDictionary:dict:

1 - (void) setProperty:(NSDictionary *)dict
2 {
3 for(NSString *key in [dict allKeys])
4 {
5 NSString *value = [dict objectForKey:key];
6 [self setValue:value forKey:key];
7 }
8 }

调用时:

1 - (instancetype)initWithDict:(NSDictionary *)dict{
2 if(self = [super init]){
3 //[self setValuesForKeysWithDictionary:dict];
4 [self setProperty:dict];
5 }
6 return self;
7 }

和setValuesForKeyWithDictionary:dict 功能是一样的。
使用setValuesForKeyWithDictionary:dict一个需要注意的地方
当字典中有某个值,而对象没有相应的属性时,会发生崩溃。比如,新的字典如下:

1 - (void)initStudent
2 {
3 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
4 @"Tom",@"name",
5 @"110",@"num",
6 @"170.0",@"height",
7 @"boy",@"sex",nil];
8 Student *stu = [[Student alloc] initWithDict:dict];
9 NSLog(@"name = %@ num = %@ height = %f",stu.name,stu.num,stu.height);
10 }

字典中有 sex属性,但是Studeng对象中没有这个属性,此时会发生崩溃。解决方法:
实现 setValue: forUndefineKey: 方法,在该方法中处理出现没有属性的情况。
student.h中添加代码:
1 - (void)setValue:(id)value forUndefinedKey:(NSString *)key;
student.m中添加代码:
1 - (void)setValue:(id)value forUndefinedKey:(NSString *)key
2 {
3
4 }
当出现没有属性的情况时,就会调用 setValue: forUndefineKey: 方法。因为该方法没做处理,所以这种情况不做处理,不会发生崩溃。需要注意的是:setValue: forUndefineKey: 方法用途很广泛,比如说字典中某个key值 为id,但是在OC中id 是关键字,这种情况也可以在 setValue: forUndefineKey: 方法中处理。
三:键值监听(KVO)
KVO全称 Key Value Observing。使用KVO可以实现视图组件和数据模型的分离,视图作为监听器,当模型的属性值发生变化后,监听器可以做相应的处理。KVO的方法由NSKeyValueObserving协议提供,同样NSObject已经实现了该协议,因此几乎所有的对象都可以使用KVO。
使用KVO操作常用的方法如下:
注册制定路径的监听器: addObserver: forKeyPath: option: context:
删除制定路径的监听器:removeObserver: forKeyPath:
触发监听时的方法:observeValueForKeyPath: ofObject: change: context:
一个KVO的例子:
有两个类: Dog类和People类
Dog.h

1 #import <Foundation/Foundation.h>
2
3 @interface Dog : NSObject
4
5 @property (nonatomic, copy) NSString *name;
6
7 @end

Dog.m
1 #import "Dog.h"
2
3 @implementation Dog
4
5 @end
People.h

1 #import <Foundation/Foundation.h>
2
3 @class Dog;
4
5 @interface People : NSObject
6
7 @property (nonatomic , copy) NSString *name;
8 @property (nonatomic , strong) Dog *dog;
9
10 @end

People.m

1 #import "People.h"
2 #import "Dog.h"
3
4 @implementation People
5
6 /**
7 * 初始化时增加对dog的监听
8 *
9 */
10 - (void)setDog:(Dog *)dog
11 {
12 _dog = dog;
13 [self.dog addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
14 }
15 /**
16 * 重写observeValueForKeyPath方法
17 */
18 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
19 {
20 if([keyPath isEqualToString:@"name"]){
21 NSLog(@"newName = %@",[change objectForKey:@"new"]);
22 }
23 }
24
25 - (void)dealloc
26 {
27 //移除监听
28 [self.dog removeObserver:self forKeyPath:@"name"];
29 }
30
31 @end

测试KVO函数:

1 - (void)testKVO
2 {
3 People *people = [[People alloc] init];
4 people.name = @"Tom";
5 Dog *dog = [[Dog alloc] init];
6 dog.name = @"Teddy";
7 people.dog = dog;
8
9 //执行到这一步后,会触发监听器的函数(observeValueForKeyPath)
10 dog.name = @"testChange";
11 }

在代码中,当修改dog 的name属性时,就会触发监听方法,也就是
observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context 方法。
iOS开发中KVC、KVO简介的更多相关文章
- iOS开发UI篇—CALayer简介
iOS开发UI篇—CALayer简介 一.简单介绍 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮.一个文本标签.一个文本输入框.一个图标等等,这些都是UIView. 其实 ...
- iOS开发中静态库制作 之.a静态库制作及使用篇
iOS开发中静态库之".a静态库"的制作及使用篇 一.库的简介 1.什么是库? 库是程序代码的集合,是共享程序代码的一种方式 2.库的类型? 根据源代码的公开情况,库可以分为2种类 ...
- iOS开发中的4种数据持久化方式【一、属性列表与归档解档】
iOS中的永久存储,也就是在关机重新启动设备,或者关闭应用时,不会丢失数据.在实际开发应用时,往往需要持久存储数据的,这样用户才能在对应用进行操作后,再次启动能看到自己更改的结果与痕迹.ios开发中, ...
- 多线程在iOS开发中的应用
多线程基本概念 01 进程 进程是指在系统中正在运行的一个应用程序.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 02 线程 2-1 基本概念 1个进程要想执行任务,必须得有线程 ...
- 解析iOS开发中的FirstResponder第一响应对象
1. UIResonder 对于C#里所有的控件(例如TextBox),都继承于Control类.而Control类的继承关系如下: 代码如下: System.Object System.Marsha ...
- 简述 Ruby 与 DSL 在 iOS 开发中的运用
阅读本文不需要预先掌握 Ruby 与 DSL 相关的知识 何为 DSL DSL(Domain Specific Language) 翻译成中文就是:"领域特定语言".首先,从定义就 ...
- iOS 开发中常见的设计模式
最近有小伙伴问到在iOS开发中的几种设计模式,这里摘录一下别人的总结(因为已经感觉总结得差不多了,适用的可以阅读一下) 首先是开发中的23中设计模式分为三大类:1.创建型 2.结构型 3.行为型 (i ...
- iOS开发中的MVC设计模式
我们今天谈谈cocoa程序设计中的 模型-视图-控制器(MVC)范型.我们将从两大方面来讨论MVC: 什么是MVC? M.V.C之间的交流方式是什么样子的? 理解了MVC的概念,对cocoa程序开发是 ...
- iOS开发多线程篇—多线程简介
iOS开发多线程篇-多线程简介 一.进程和线程 1.什么是进程 进程是指在系统中正在执行的一个应用程序 每一个进程之间是独立的.每一个进程均执行在其专用且受保护的内存空间内 比方同一时候打开QQ.Xc ...
随机推荐
- 有了 indexOf,为什么 ECMAScript 7 还添加了 Array.prototype.include
ECMAScript 7 中新增了用于检测数组中是否包含某个元素 Array.prototype.includes() API,想到了 Array 其实有很多相关 API 可以检测到是否包含某个元素, ...
- Openfire分析之三:ConnectionManager 连接管理(1)
Openfire是怎么实现连接请求的? XMPPServer.start()方法,完成Openfire的启动.但是,XMPPServer.start()方法中,并没有提及如何监听端口,那么Openfi ...
- python专题-爬虫功能
在我们日常上网浏览网页的时候,经常会看到一些好看的图片,我们就希望把这些图片保存下载,或者用户用来做桌面壁纸,或者用来做设计的素材. 我们最常规的做法就是通过鼠标右键,选择另存为.但有些图片鼠标右键的 ...
- MEF IOC使用
IOC介绍 IOC:控制反转,DI:依赖注入.按我的理解应该是一个东西.作用目前我看到的主要是解除各个层之间的强耦合,实现接口分离.MEF优点: 1.net4 自带,无需安装扩展(引用System.C ...
- github+hexo搭建自己的博客网站(六)进阶配置(搜索引擎收录,优化你的url)
详细的可以查看hexo博客的演示:https://saucxs.github.io/ 绑定了域名: http://www.chengxinsong.cn hexo+github博客网站源码(可以clo ...
- 慕课网视频破解付费分享-前端开发-Python等
微信小程序 慕课网 BAT大牛经验总结全面深入解读Android面试 前端JS基础面试技巧 vue2.0+node.js+mongodb全栈打造商城 Vue.js高级实战-开发移动端音 ...
- 浅谈java中==与equals的区别
今天做了一个业务模块,需要简单的遍历比较值,所以习惯性的用了 "==" ,但是结果没有达到预想的结果是什么鬼? 看到这里,有人一定会指出这俩货不是基本变量! "关系操作符 ...
- jenkins+gitlab+sonar+testng构建持续集成测试环境(配置干货篇)
几个工具的安装部分就不在此介绍了! jenkins配置: 1.插件安装 2.root私钥配置 3.构建job配置 4.部署job配置 5.测试job配置 7.jenkins全局工具配置 8.jenki ...
- CSS3弹性盒模型 display:box
刚开始做网页时就有一个困惑,为什么display:block只能垂直排列,如果要水平排列就要使用float:left等方式.这种方法最难受的当然是当子元素的数量改变时,需要去修改子元素的宽度使重新适应 ...
- java环境安装说明
Java从安装到运行第一个程序 对于初学者来说,能否成功运行第一个Java程序,关系到这杯咖啡的口感. 作为才疏学浅的常年初学者,语言描述不清,还是上图吧! 一.安装JDK 打开网址http://ww ...