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 的属性设置方式来变更属性值。

OC - KVO实现原理的更多相关文章

  1. iOS KVO的原理

    KVO(Key Value Observing),是观察者模式在Foundation中的实现.   KVO的原理   简而言之就是:   1.当一个object有观察者时,动态创建这个object的类 ...

  2. KVC, KVO 实现原理

    Key-Value Coding: 键值编码 (KVC) 方法调用: // 对象属性 // 类似: Person -> name setValue: forKey: // 对象的属性或者 属性的 ...

  3. 使用Runtime自定义KVO,原理浅析

    一.介绍 什么是KVO?全称key-value-observer,键值观察,观察者设计模式的另一种实现.其作用是通过观察者监听属性值的变化而做出函数回调. 二.原理 KVO基于Runtime机制实现, ...

  4. KVC, KVO实现原理剖析

    iPhone程序开发 KVO/KVC实现机理分析是本文要介绍的内容,不多说,直接进入话题.我们来看详细内容. Objective-C里面的Key-Value Observing (KVO)机制,非常不 ...

  5. KVO 其原理探究

    什么是KVO ? KVO这是Key-Value Observing,它提供了一种机制,指定的对象的属性被改动后,则对象就会接受到通知. 简单的说就是每次指定的被观察的对象的属性被改动后.KVO就会自己 ...

  6. KVO实现原理

    KVO意为键值观察Key-Value-Observer,本质仍然是观察者模式. 观察者模式的定义:一个目标对象管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象.这个主动通知通常是 ...

  7. OC SEL (@selector) 原理及使用总结(转)

    SEL 类成员方法的指针 可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应 ...

  8. 42 (OC)* 字典实现原理--哈希原理

    一.NSDictionary使用原理 1.NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的,hash函数设计的好坏影响着数据的查找访问效率. - (void ...

  9. OC - 17.AFNetworking原理及常用操作

    AFN的六大模块 NSURLConnection,主要对NSURLConnection进行了进一步的封装,包含以下核心的类: AFURLConnectionOperation AFHTTPReques ...

随机推荐

  1. [Java]private, public,protected,friendly的区别(转载)一下子就记住了

    http://teddyboy200382.blog.163.com/blog/static/320112002008825112549780/ 说明这四个关键字之前,我想就 class 之间的关系做 ...

  2. 07_dubbo_compiler

    [开始解析最后一行代码 ExtensionLoader.getAdaptiveExtension()] ExtensionLoader<Protocol> loader = Extensi ...

  3. babel的使用以及安装配置

    简介 babel是一个广泛使用的转码器,可以将ES6代码转化为ES5代码,从而在现有环境执行,这意味着,你可以现在就用ES6编写程序,而不用担心现有环境是否支持. 安装及配置 npm install  ...

  4. 定制化移动办公APP:打造企业专属的“钉钉”“纷享销客”,实现企业办公管理一体化

    一.项目背景 随着信息化社会的高速发展,市场竞争日益激烈,传统的管理和办公系统多且复杂,用户需要使用多个系统才可完成一项工作,而且各个系统的界面和风格存在差异,造成了信息查找不便,大大降低了用户的工作 ...

  5. 使用intellij idea搭建MAVEN+SSM(Spring+SpringMVC+MyBatis)框架

    基本概念 使用SSM(Spring,SpringMVC和Mybatis) 1.1.Spring Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod  ...

  6. Java任务调度框架Quartz入门

    Quartz[kwɔːts]:石英,其框架和名字一样简单朴素又不失魅力,在Java程序界,Quartz大名鼎鼎,很多Java应用几乎都集成或构建了一个定时任务调度系统,Quartz是一个定时任务调度框 ...

  7. Java中short、int、long、float、double的取值范围

    一.基本数据类型的特点,位数,最大值和最小值.1.基本类型:short 二进制位数:16 包装类:java.lang.Short 最小值:Short.MIN_VALUE=-32768 (-2的15此方 ...

  8. ZT北大青鸟营业额超20亿到不值一提 衰落的背后

    北大青鸟营业额超20亿到不值一提 衰落的背后 2013-10-18 08:18 王根旺  我要评论 (0) “北大青鸟是个悲剧!”说到曾经的IT培训业巨头,黑马导师.珍品网创始人曹允东惋惜道.在学大创 ...

  9. Windows文件批量重命名

    选择要命名的文件 按F2,编辑名字 然后按回车就行了 电视剧命名,我认为这样足够了

  10. codeforces 814D An overnight dance in discotheque

    题目链接 正解:贪心. 首先我们可以计算出每个圆被多少个圆覆盖. 很显然,最外面的圆是肯定要加上的. 然后第二层的圆也是要加上的.那么第三层就不可能被加上了.同理,第四层的圆又一定会被加上. 然后我们 ...