KVO键值观察的具体实现
1.KVO简介
KVO是Objective-C对观察者设计模式的一种实现,它提供一种机制,指定一个被观察对象(如A类),当对象中的某个属性发生变化的时候,对象就会接收到通知,并作出相应的处理。在MVC设计架构下的项目,KVO机制很适合实现mode模型和view视图之间的通讯。例如:代码中,在模型类A创建属性数据,在控制器中创建观察者,一旦属性数据发生改变就收到观察者收到通知,通过KVO再在控制器使用回调方法处理实现视图B的更新;
2.实现原理
KVO的实现依赖于Objective-C强大的runtime,KVO的底层实现是监听setter方法。当观察某对象A时,KVO动态机制会动态创建一个A类的子类,并为这个新的子类重写父类的的setter方法。setter方法随后负责通知观察对象属性的变化。
3.深入理解
Apple使用了isa混写(isa-swizzling)来实现KVO,当观察对象A的时候(也就是调用对象A注册观察者的方法的时候),KVO机制会动态创建一个A的新的子类:NSKVONotifying_A的新,该类继承自对象A的本类。且KVO会为NSKVONotifying_A重写其父类的setter方法,setter方法会负责在调用原setter方法之前或之后,通知所有观察对象属性值的更改状况。
addobserver方法内部实现:
在这个方法中,被观察对象的isa指针从指向原来的A类,被KVO机制修改为指向A类的子类-NSKVONotifying_A,来实现当前类属性值改变的监听。
isa指针的作用:每个对象都有一个isa指针,指向该对象的类。它告诉Runtime系统这个对象的类是什么,所以对象注册为观察者的是时候,isa指针会指向新的子类,那么这个对象就会变成新的子类的对象了。因此,该对象调用setter就会调用已经重写的setter了,从而激活键值通知机制。
子类setter方法剖析:
KVO的键值观察通知依赖于NSObject的两个方法:willChangeValueForKey didChangeValueForKey,在存取值的前后分别调用的方法。被观察属性发生改变之前,willChangeValueForKey:被调用,通知系统该 keyPath 的属性值即将变更;当改变发生后, didChangeValueForKey: 被调用,通知系统该 keyPath 的属性值已经变更;之后, observeValueForKey:ofObject:change:context: 也会被调用。且重写观察属性的setter 方法这种继承方式的注入是在运行时而不是编译时实现的。
4.自己实现KVO
首先创建一个NSObject的类扩展,.h文件中自定一个一个方法,由于目标是自定义实现KVO,所以只需要在系统添加观察者的方法名前面添加一个前缀,参数值不变。方法实现如下
- (void)CC_addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid*)context{
/*
1.自定义一个NSCCKVO_XXX子类
2.重写父类的setter,在内部恢复子类的做法,通知观察者
3.修改self的指针,指向新创建的NSCCKVO_XXX子类
*/
//动态生成一个类
NSString*oldClassName =NSStringFromClass([selfclass]);
NSString*newClassName = [@"NSCCKVO_"stringByAppendingString:oldClassName];
constchar* name= [newClassNameUTF8String];
//定义一个类
//参数1:继承的那个类参数2:类名称
Class ccClass=objc_allocateClassPair([selfclass], name,0);
//子类添加setter方法,以setName为例
class_addMethod(ccClass,@selector(setName:), (IMP)setName,"v@:@");
//注册这个类
objc_registerClassPair(ccClass);
//修改self的isa指针
object_setClass(self, ccClass);
//绑定observer到self对象中,将观察者绑定当前对象
objc_setAssociatedObject(self, (__bridgeconstvoid*)@"objc", observer,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
voidsetName(idself,SEL_cmd,NSString*newName){
//调用父类的sett方法
Class superClass =class_getSuperclass([selfclass]);
//改变isa指针。为父类,调用set方法
object_setClass(self, superClass);
objc_msgSend(self,@selector(setName:),newName);
//拿出观察者
idobserver =objc_getAssociatedObject(self, (__bridgeconstvoid*)@"objc");
//通知观察者
objc_msgSend(observer,@selector(observeValueForKeyPath:ofObject:change:context:),@"name",self,@{@"new":newName},nil);
//改回子类类型
object_setClass(self, [selfclass]);
}
然后在ViewController导入NSObject的类扩展的头文件
Person*person = [[Personalloc]init];
_person= person;
//[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
// 这个方法就是刚才自己去实现的那个方法[personCC_addObserver:selfforKeyPath:@"name"options:NSKeyValueObservingOptionNewcontext:nil];
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context{
NSLog(@"%@的%@值变成了%@",object,keyPath,change[@"new"]);
}
此处,自己实现KVO就完成了。观察者观察的是属性,只有遵循 KVO 变更属性值的方式才会执行KVO的回调方法,例如是否执行了setter方法、或者是否使用了KVC赋值。如果赋值没有通过setter方法或者KVC,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName",这时是不会触发kvo机制,更加不会调用回调方法的。所以使用KVO机制的前提是遵循 KVO 的属性设置方式来变更属性值。
KVO键值观察的具体实现的更多相关文章
- K-V-O 键值观察机制
在两个不同的控制器之间传值是iOS开发中常有的情况,应对这种情况呢,有多种的应对办法.kvc就是其中的一种,所以,我们就在此解释之. key value observing 键值观察,给人一种高 ...
- iOS - KVO 键值观察
1.KVO KVO 是 Key-Value Observing 的简写,是键值观察的意思,属于 runtime 方法.Key Value Observing 顾名思义就是一种 observer 模式用 ...
- KVO - 键值观察
[基本概念] 键值观察是一种使对象获取其他对象的特定属性变化的通知机制.控制器层的绑定技术就是严重依赖键值观察获得模型层和控制器层的变化通知的.对于不依赖控制器层类的应用程序,键值观察提供了一种简化的 ...
- KVO(键-值观察)
// 1.键-值观察 // 2.它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知. // 3.符合KVC(Key-ValuedCoding)机制的对象才可以使用KVO // 4.实现过 ...
- OC键值观察KVO
什么是KVO? 什么是KVO?KVO是Key-Value Observing的简称,翻译成中文就是键值观察.这是iOS支持的一种机制,用来做什么呢?我们在开发应用时经常需要进行通信,比如一个model ...
- 路径(keyPath)、键值编码(KVC)和键值观察(KVO)
键路径 在一个给定的实体中,同一个属性的所有值具有相同的数据类型. 键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制. - 键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接 ...
- 09 (OC)* 键路径(keyPath)、键值编码(KVC)、键值观察(KVO)
键路径在一个给定的实体中,同一个属性的所有值具有相同的数据类型.键-值编码技术用于进行这样的查找—它是一种间接访问对象属性的机制. - 键路径是一个由用点作分隔符的键组成的字符串,用于指定一个连接在一 ...
- 深度理解Key-Value Observing 键值观察
前言 在上一阶段的开发过程中,我们大量使用了 KVO 机制,来确保页面信息的及时同步.也因此碰到了很多问题,促使我们去进一步学习 KVO 的相关机制,再到寻找更好的解决方案.鉴于 KVO 让人欲仙 ...
- [深入浅出Cocoa]详解键值观察(KVO)及其实现机理
一,前言 Objective-C 中的键(key)-值(value)观察(KVO)并不是什么新鲜事物,它来源于设计模式中的观察者模式,其基本思想就是: 一个目标对象管理所有依赖于它的观察者对象,并在它 ...
随机推荐
- ThinkPHP 5 中AJAX跨域请求头设置方法
最近用thinkphp做项目,在测试环境时,存在接口的测试问题.在tp官网也没能找到相关的解决方法.自已看了一下源码,有如下的解决方案. 在项目目录下面,创建common/behavior/CronR ...
- Redis在centos6.4上的最详细图文安装教程
准备工作:一个redis3.0.0的安装包,没有可以点击下面的链接下载 https://pan.baidu.com/s/1kU5Ez2J 工具 安装环境 centos6.4 好了开始进入正题 输入 ...
- 伸缩的菜单,用toggle()重写
<!DOCTYPE ><html><head><meta charset="UTF-8"/><title>伸缩的菜单,用 ...
- linux进程管理和系统状态查看命令简介
1 进程管理简介 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础 2 常用命令 2.1 pstree 2.1.1 功能描 ...
- C++ vector 常用API
vector: 向量容器,动态数组,类模板 定义和初始化: vector<T> v1; //v1是空vector,元素类型是T类型,执行默认初始化,int为0,string为空串 vect ...
- 程序员的自我救赎---3.2:SSO及应用案例
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- 免费靠谱的 Let’s Encrypt 免费 https 证书申请全过程
申请 Let’s Encrypt证书的原因: 现在阿里云等都有免费的 https 证书,为什么还要申请这个呢(估计也是因为阿里云这些有免费证书的原因,所以 Let’s Encrypt 知道的人其实并不 ...
- 基于 Vue.js 的移动端组件库mint-ui实现无限滚动加载更多
通过多次爬坑,发现了这些监听滚动来加载更多的组件的共同点, 因为这些加载更多的方法是绑定在需要加载更多的内容的元素上的, 所以是进入页面则直接触发一次,当监听到滚动事件之后,继续加载更多, 所以对于无 ...
- 玩了几天的ARToolKit
因为玩的不深,就说一说配置环境出错的解决. 假如大家网上下载的ARToolKit下面有glut32.dll,那按照教程来配置环境的时候(其中有一步是:将OpenGL的dll拷贝到windows sys ...
- Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Query was empty
1 错误描写叙述 at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(Invocable ...