苹果的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. 源码包安装mysql5.6

    含有的命令:wget,tar,cp,groupadd,useradd,chown,service,chkconfig,exportsource,mysql_secure_installation,my ...

  2. 引用:使用grunt 压缩 合并js、css文件

    引用:https://www.jianshu.com/p/08c7babdec65 压缩 js 文件 1.创建一个目录 名为grunt   目录.png 2.在grunt目录下创建一个 src目录,存 ...

  3. 上传及更新代码到github(以及如何在vscode上提交自己的代码)

    上传本地代码 第一步:去github上创建自己的Repository,创建页面如下图所示: 红框为新建的仓库的https地址 第二步: echo "# Test" >> ...

  4. python to shell vimdiff

    目录 #!/bin/python3 import os import sys if(len(sys.argv) != 3): exit ("Usage: argv1:fullPath.txt ...

  5. 第五周ip通信基础回顾

    广播请求,单播响应,ARP IPV4,IP地址32位二进制代码分为8个位一组 路由器每一个接口都是一个网段 ,网段与网段区分看网络地址 同一段链路是同网段 直接广播:主机号全为1 受限广播:全为1 特 ...

  6. 2018-2019-2 20165225《网络对抗技术》Exp1 缓冲区溢出实验

    2018-2019-2 20165225<网络对抗技术>Exp1 缓冲区溢出实验 声明 虽然老师在邮箱中要求要把虚拟机名改为个人名字缩写,但是我的kali好像不是很听话...重启数次也没用 ...

  7. GridFS and Geospatial

    GridFS - specification for storing and retrieving large files... images,audio file, video files... F ...

  8. 透过实现小型打包工具理解webpack

    面试经常有问到 webpack,偶遇一篇比较有实用价值的且有利于理解的文章,现总结如下: 本篇文章中要实现的这个迷你打包工具,它主要能实现如下两个功能: ①.将 ES6 转换成 ES5: ②.支持在 ...

  9. Django---请求、响应

    Django的请求和响应 一.客户端向服务器的请求简介 二.响应对象构造 回到顶部 一.客户端向服务器的请求简介 1.c-->s的传参的4中途径 提取的URL的特定部分,如/天气/北京/ 201 ...

  10. vue/cli3 配置相对路径

    根目录下新建 vue.config.js 文件 const path = require('path') function resolve(dir){ return path.join(__dirna ...