1、KVO是基于Runtime机制实现的;

2、当某个类的对象的某个属性第一次被观察时,系统会在运行期间动态地创建该类的一个派生类,在这个派生类中重写基类的任何被观察属性的setter方法,派生类在被重写的setter方法内实现真正的通知机制;

3、如果原类为Person,那么生成的派生类名为NSKVONotifying_Person;

4、我们知道,每一个对象都有一个isa指针(即第一个成员变量)指向当前类,所以系统就是在当一个类的对象第一次被观察的时候,偷偷的将isa指针指向动态生成的派生类,从而在 “被监听属性” 赋值时执行的是派生类的setter方法;

5、此外,苹果还偷偷重写了此派生类的class方法,这时候 po personObj,打印的依旧是Person,但使用po object_getClass(personObj),就可以打印出对象的类型NSKVONotifying_Person;这是苹果为了隐藏生成的派生类,让我们误以为还是使用的当前类;

6、键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就会记录旧的值;而当改变发生后,didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。

7、注意:willChangeValueForKey:和didChangevlueForKey:缺一不可;

直接拿代码来说明吧,说了一大堆,估计大家都看的不知所以然。

1、场景:我们声明了一个Person类,里面有两个属性,name和age,我们要使用kvo来监听Person对象的age发生的变化;

2、创建Person类,声明属性:

 #import <Foundation/Foundation.h>

 /**
Person模型类
*/
@interface Person : NSObject /**
姓名
*/
@property(nonatomic,copy) NSString *name; /**
年龄
*/
@property(nonatomic,assign) NSInteger age; @end

3、创建Person对象,添加KVO监听对象age属性

 #import "ViewController.h"
#import "Person.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; Person *per = [[Person alloc] init];
per.name = @"xiaoming";
per.age = ; // 观察per对象的age属性的变化情况
[per addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:NULL]; // 修改age属性的值,将会被观察到
per.age = ; // 这里po per的结果:<Person: 0x608000026960>
// 但是:po object_getClass(per)的结果:NSKVONotifying_Person
// 所以:苹果偷偷重写了派生类的class方法,但用运行时可以查看当前对象所属类型 // 移除KVO
[per removeObserver:self forKeyPath:@"age"];
} /**
KVO监听:当被监听对象的被监听属性变化后调用 @param keyPath 被监听的属性
@param object 被监听的对象
@param change 变化情况
@param context 携带的参数,此示例传入NULL即可
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@对象的%@属性发生变化了%@",object,keyPath,change); // 打印结果:
// <Person: 0x608000026960>对象的age属性发生变化了{
// kind = 1;
// new = 20;
// }
} @end

其实,实现KVO的是这2个方法:willChangeValueForKey:和didChangevlueForKey:

我们只要调用这两个方法,属性值不变化,也会被观察到。

1、给Person类添加一个方法kvoTest:

 #import <Foundation/Foundation.h>

 /**
Person模型类
*/
@interface Person : NSObject /**
姓名
*/
@property(nonatomic,copy) NSString *name; /**
年龄
*/
@property(nonatomic,assign) NSInteger age; /**
监听age属性发生变化
*/
- (void)kvoTest; @end
 #import "Person.h"

 @implementation Person

 - (void)kvoTest{

     [self willChangeValueForKey:@"age"];

     [self didChangeValueForKey:@"age"];
} @end

2、调用kvoTest方法监听对象属性age的变化:

 #import "ViewController.h"
#import "Person.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; Person *per = [[Person alloc] init];
per.name = @"xiaoming";
per.age = ; // 观察per对象的age属性的变化情况
[per addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:NULL]; // kvoTest里面调用了willChangeValueForKey:和didChangeValueForKey
// 所以:不改变age的值,依旧会被监听到
[per kvoTest]; // 这里po per的结果:<Person: 0x608000026960>
// 但是:po object_getClass(per)的结果:NSKVONotifying_Person
// 所以:苹果偷偷重写了派生类的class方法,但用运行时可以查看当前对象所属类型 // 移除KVO
[per removeObserver:self forKeyPath:@"age"];
} /**
KVO监听:当被监听对象的被监听属性变化后调用 @param keyPath 被监听的属性
@param object 被监听的对象
@param change 变化情况
@param context 携带的参数,此示例传入NULL即可
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@对象的%@属性发生变化了%@",object,keyPath,change); // 打印结果:
// <Person: 0x608000032ca0>对象的age属性发生变化了{
// kind = 1;
// new = 18; // 因为age的值根本没有发生变化,所是这里的new还是18
// }
} @end

KVO的底层实现的更多相关文章

  1. KVO的底层实现原理?如何取消系统默认的KVO并手动触发?

    KVO是基于runtime机制实现的 当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类(该类的子类),在这个派生类中重写基类中任何被观察属性的setter 方法.派生类在被 ...

  2. Object-C知识点 (三) 单例 蒙版 刷新 KVO底层

    #pragma mark - 单例方法(完整的方法) 系统的单例方法名称 sharedApplication defaultManager standardUserDefaults currentDe ...

  3. KVO底层实现原理,仿写KVO

    这篇文章简单介绍苹果的KVO底层是怎么实现的,自己仿照KVO的底层实现,写一个自己的KVO监听 #pragma mark--KVO底层实现 第一步:新建一个Person类继承NSObject Pers ...

  4. 简单总结一下NotificationCenter、KVC、KVO、Delegate

    将最近总结的最常用的几种设计模式优势与区别自己小结一下,分享给大家. kvo只能用来对属性作出反应,而不会用来对方法或者动作作出反应,是实现一个对象与另外一个对象保持同步的一种方法,能够提供观察的属性 ...

  5. KVO键值观察的具体实现

    1.KVO简介 KVO是Objective-C对观察者设计模式的一种实现,它提供一种机制,指定一个被观察对象(如A类),当对象中的某个属性发生变化的时候,对象就会接收到通知,并作出相应的处理.在MVC ...

  6. KVO讲解

    最近一直在写swift项目,没有时间更新自己的技术博客,以前在博客里面写过KVO的底层原理,今天我们来看一下KVO的整个使用过程和使用场景(附有demo),大约花大家10-15分钟时间,希望大家看完博 ...

  7. 刨根问底KVO原理

    介绍 KVO( NSKeyValueObserving )是一种监测对象属性值变化的观察者模式机制.其特点是无需事先修改被观察者代码,利用 runtime 实现运行中修改某一实例达到目的,保证了未侵入 ...

  8. OC - KVO实现原理

    1.KVO简介 KVO是Objective-C对观察者设计模式的一种实现,它提供一种机制,指定一个被观察对象(如A类),当对象中的某个属性发生变化的时候,对象就会接收到通知,并作出相应的处理.在MVC ...

  9. iOS:KVC和KVO

    来源:  对月流 链接:http://www.jianshu.com/p/f1393d10109d 写在前面: 关于KVC和KVO各种博客多了去了,重新整理下,就当是温习一下吧,也还算是个新手,不对的 ...

随机推荐

  1. SQL Server中的事务日志管理(9/9):监控事务日志

    当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...

  2. 第19/24周 锁升级(Lock Escalations)

    大家好,欢迎回到性能调优培训.上2个星期我们已经讨论了SQLServer里的悲观和乐观锁.今天我想谈下SQL Server里对于锁的一个特殊现象:所谓的锁升级(Lock Escalations).在我 ...

  3. 安装logstash,elasticsearch,kibana三件套

    logstash,elasticsearch,kibana三件套 elk是指logstash,elasticsearch,kibana三件套,这三件套可以组成日志分析和监控工具 注意: 关于安装文档, ...

  4. Elasticsearch 教程--入门

    1.1 初识 Elasticsearch 是一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎,可以说 Lucene 是当今最先进,最高效的全功能开源搜索引擎框架. 但是 L ...

  5. [译]学习IPython进行交互式计算和数据可视化(七)

    第六章:定制IPython 对于高级用户,IPython可以进行定制和扩展.在本章结束之后,你将会知道: 怎样创建和使用自定义配置文件 怎样为高级功能进行IPython扩展 怎样在notebook中使 ...

  6. 前端代码标准最佳实践:CSS

    前端工程师对写标准的前端代码的重视程度很高.这些最佳标准实践并不是那个权威组织发布的,而是由大量的前端工程师们在实践过程中的经验总结,目的在于提高代码的可读性,可维护性和性能.那么接着上一篇,我们再来 ...

  7. Nancy 学习-身份认证(Forms authentication) 继续跨平台

    开源 示例代码:https://github.com/linezero/NancyDemo 上篇讲解Nancy的Basic Authentication,现在来学习Nancy 的Forms身份认证. ...

  8. C#调用Java方法(详细实例)

    C#可以直接引用C++的DLL和转换JAVA写好的程序.最近由于工作原因接触这方面比较多,根据实际需求,我们通过一个具体例子把一个JAVA方法转换成可以由C#直接调用的DLL C#调用c++ C#调用 ...

  9. Firemonkey 图片显示拉伸不变形

    Firemonkey 实现简单的图片拉伸不变形,是利用原始图片的 "固定区" 及 "位伸区" 来达到此目的,因此必需要有此结构的图片才适合. 下面以聊天气泡为例 ...

  10. 内存只有4G的MBP要怎么破

    开发工具包括浏览器都是极占内存的,没有个8G根本不行啊. 怎一个慢字了得? 补记: 放弃谷歌浏览器是正道