苹果的KVO原理通过isa-swizzling技术实现,本质实现逻辑是在runtime时添加一个子类,重写set方法进行操作,现在我们也基于runtime来实现一个KVO。

首先新建一个Person类,继承自NSObject,添加一个name属性。

然后给NSObject添加一个分类KVO,在分类中实现KVO的注册方法EZ_addObserver:forKeyPath: options:context:,这个方法的作用和系统的注册方法一样。先为Person动态添加一个子类EZKVO_Person,然后为其添加一个setName方法,因为我们要监听的是那么的变化。

- (void)EZ_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
// 动态添加一个类
NSString *className = [@"EZKVO_" stringByAppendingString:NSStringFromClass([self class])];
const char *utfName = [className UTF8String];
Class myClass = objc_allocateClassPair([self class], utfName, ); // 添加setter方法
class_addMethod(myClass, @selector(setName:), (IMP)setName, "v@:i"); // 注册新添加的这个类
objc_registerClassPair(myClass); // 修改被观察者的isa指针,isa指针指向Person类改为指向myclass类
object_setClass(self, myClass); // 将观察者的属性保存到当前类里面去
objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

上面的是注册方法,下面处理的是在更改name属性值的时候,通过重写的set方法,将消息发出给观察者让其收到键值变化。过程是先取出被观察者,然后发送消息(即observeValueForKeyPath:ofObject:change:context:)让观察者接收。当然,也可以逻辑严谨些像系统那样向父类逐层调用,这里就不写出来了。

void setName(id self, SEL _cmd, id name) {
// 取出观察者
id objc = objc_getAssociatedObject(self, (__bridge const void *)@"objc"); // 通知观察者
((void(*)(id,SEL,id,id,id,id))objc_msgSend)(objc, @selector(observeValueForKeyPath:ofObject:change:context:), name, self, nil, nil);
}

最后沿用前面两篇文章的方法,在VC里添加触摸事件改变name的值,并在消息接收方法里打印信息(方便测试,就用了keyPath来代替change保存键值)。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.person.name = @"小明";
}

消息接收

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"name属性的改变为%@",keyPath);
}

结果:2018-07-22 23:29:49.085057+0800 KVODemo[4402:212515] name属性的改变为小明

KVO的使用三:基于runtime实现KVO的更多相关文章

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

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

  2. 《深入浅出Windows Phone 8.1 应用开发》基于Runtime框架全新升级版

    <深入浅出Windows Phone 8.1 应用开发>使用WP8.1 Runtime框架最新的API重写了上一本<深入浅出Windows Phone 8应用开发>大部分的的内 ...

  3. oc是一个全动态语言,oc的一切都是基于runtime实现的!

    oc是一个全动态语言,oc的一切都是基于runtime实现的! 从以下三方面来理解runtime吧! 1. 传统的面向过程的语言开发,例如c语言.实现c语言编译器很简单,只要按照语法规则实现一个LAL ...

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

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

  5. Keil MDK STM32系列(三) 基于标准外设库SPL的STM32F407开发

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  6. word2vec原理(三) 基于Negative Sampling的模型

    word2vec原理(一) CBOW与Skip-Gram模型基础 word2vec原理(二) 基于Hierarchical Softmax的模型 word2vec原理(三) 基于Negative Sa ...

  7. [iOS]一行代码集成空白页面占位图(基于runtime+MJRefresh思想)

    2018年01月03日阅读 2472   [iOS]一行代码集成空白页面占位图(基于runtime+MJRefresh思想) LYEmptyView 此框架是本人在5,6个月前,公司启动新项目的时候, ...

  8. {django模型层(二)多表操作}一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询、分组查询、F查询和Q查询

    Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 xxx 七 ...

  9. STC8H开发(三): 基于FwLib_STC8的模数转换ADC介绍和演示用例说明

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

随机推荐

  1. request之LIstener监听器

    要实现监听request内置对象,必须实现一个接口javax.servlet.ServletRequsetListener. 代码如下: package cn.wangkai.listener; im ...

  2. 简单数据库开发之dao层开发

    数据库 dao层是用来与底层数据库连接的一系列代码,它因上层service层调用而调用底层数据库,因为一般的数据库不会只存在一到几张表格,所以必须定义出dao层的接口协议,方便各种表格的操作. dao ...

  3. 托马斯微积分13版原版pdf

    泰国一大学的网站? 里边有好多书-- 悄悄的,不要外传-- http://www.maths.sci.ku.ac.th/suchai/417167/thomas.pdf

  4. pc端 页面 显示在手机 一行控制适配问题

    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">

  5. MySQL中实现连续日期内数据统计,缺省天数0补全

    某一日,需要查询订单表中一个月每天的金额数 查询出数据如下: array(14) { [0] => array(2) { ["money"] => string(7) ...

  6. 立一个Flag吧

    以后在学习上遇到的问题以及解决办法,大部分都要记录在这里了,以备温故知新....就这样吧!

  7. - configuration.module has an unknown property 'loader' 问题解决

    错误提示: Invalid configuration object. Webpack has been initialised using a configuration object that d ...

  8. git三、上传项目到github

    1.创建github仓库 2.git clone url (克隆仓库到本地,如profect) 3.将项目复制到本地文件夹profect下 4.git add . (添加项目至缓存区) 5.git c ...

  9. 【Python学习】iterator 迭代器小练习

    http://anandology.com/python-practice-book/iterators.html  Problem 1: Write an iterator class revers ...

  10. 从拥抱开源到回馈开源,灵雀云助力CNCF中国区培训业务

    6月27日,全球首屈一指的开源盛会 2018 LinuxCon + ContainerCon + CloudOpen China (LC3)在中国北京国家会议中心落下帷幕.二度落地中国的LC3大会热度 ...