手动实现KVO/KVO底层原理

git demo地址

看图

首先说一下Apple KVO的底层实现吧

内部实现原理
KVO是基于runtime机制实现的,运用了一个isa-swizzling技术. isa-swizzling就是类型混合指针机制, 将2个对象的isa指针互相调换.
当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制
如果原类为MyPerson,那么生成的派生类名为NSKVONotifying_MyPerson
每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法
键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
补充:KVO的这套实现机制中苹果还偷偷重写了class方法,让我们误认为还是使用的当前类,从而达到隐藏生成的派生类

开始手动实现KVO

1. 创建一个MyPerson类, 定义一个name属性,
2. 创建一个NSKVONotifying_MyPerson类继承自MyPerson, 然后重写父类的setName方法, 监听的值改变时, 回调通知观察者
- (void)setName:(NSString *)name
{[super setName:name];// 监听的值改变时, 回调通知观察者id observer = objc_getAssociatedObject(self, @"observer");id keyPath = objc_getAssociatedObject(self, @"keyPath");id context = objc_getAssociatedObject(self, @"context");if (observer) {NSDictionary *dic = @{@"new" : name};[observer nyl_observeValueForKeyPath:keyPath ofObject:self change:dic context:nil];}
}
3.创建NSObject+KVO分类, 添加nyl_addObserver...方法, 使用当前对象的isa指向新的派生类, 就会调用派生类的set方法
//
//  NSObject+KVO.m
//  手动实现KVO
//
//  Created by 聂银龙 on 2020/7/26.
//  Copyright © 2020 聂银龙. All rights reserved.
//#import "NSObject+KVO.h"
#import "NSKVONotifying_MyPerson.h"
#import <objc/runtime.h>@implementation NSObject (KVO)- (void)nyl_addObserver:(NSObject *)observerforKeyPath:(NSString *)keyPathoptions:(NSKeyValueObservingOptions)optionscontext:(nullable void *)context
{// 使用当前对象的isa指向新的派生类(NSKVONotifying_MyPerson), 就会调用派生类的set方法object_setClass(self, [NSKVONotifying_MyPerson class]);// 给分类添加属性objc_setAssociatedObject(self, @"observer", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);objc_setAssociatedObject(self, @"keyPath", keyPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);objc_setAssociatedObject(self, @"context", [NSString stringWithFormat:@"%@", context], OBJC_ASSOCIATION_RETAIN_NONATOMIC);}- (void)nyl_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context
{// 移除objc_setAssociatedObject(self, @"observer", nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);objc_setAssociatedObject(self, @"keyPath", nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);objc_setAssociatedObject(self, @"context", nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}- (void)nyl_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{NSLog(@"子类去实现");}@end
4 使用

添加观察者

 [self.person nyl_addObserver:self forKeyPath:@"name" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:nil];

改变self.person.name的值

self.person.name = @"张飞";

观察回调方法

- (void)nyl_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{NSLog(@"change = %@", change); // change = {new = "张飞";}self.nameLabel.text = change[@"new"];
}

git demo地址

iOS 手动实现KVO / iOS KVO底层原理相关推荐

  1. iOS之深入解析类加载的底层原理:类如何加载到内存中

    一.App 启动与 dylb 加载 App 启动会由 libdyld.dylib 库先于 main 函数调用 start,执行 _dyld_start 方法,然后运用汇编实现调用 dyldbootst ...

  2. iOS之深入解析KVC的底层原理和自定义KVC的实现

    一.KVC 简介 ① 定义 KVC 是 Key-Value Coding 的简称,中文译义为键值编码. KVC 是指 iOS 的开发中,可以允许开发者通过 Key 名直接访问对象的属性,或者给对象的属 ...

  3. iOS之深入解析Runloop的底层原理

    一.Runloop 简介 ① 什么是 Runloop ? RunLoop 是事件接收和分发机制的一个实现,是线程相关的基础框架的一部分,一个 RunLoop 就是一个事件处理的循环,用来不停的调度工作 ...

  4. iOS之深入解析AFNetworking的底层原理

    AFNetworking 简介 一.AFNetworking 版本 AFNetworking 是 iOS 最常用的网络框架,虽然系统也有 NSURLSession ,但是一般不会直接用它.AFNetw ...

  5. iOS之深入解析malloc的底层原理

    一.前言 iOS 在创建对象的时候,alloc 方法有三个核心部分:cls->instanceSize(计算需要开辟内存的大小),calloc(开辟内存空间),obj->initlnsta ...

  6. iOS之深入解析渲染的底层原理

    一.计算机渲染原理 ① CPU 与 GPU 的架构 对于现代计算机系统,简单来说可以大概视作三层架构:硬件.操作系统与进程.对于移动端来说,进程就是 App,而 CPU 与 GPU 是硬件层面的重要组 ...

  7. iOS之深入解析“锁”的底层原理

    一.OSSpinLock(自旋锁) 自从 OSSpinLock 出现安全问题,在 iOS10 之后就被 Apple 废弃.自旋锁之所以不安全,是因为获取锁后,线程会一直处于忙等待,造成了任务的优先级反 ...

  8. iOS之深入解析GCD的底层原理

    一.队列 ① 队列实现源码分析 在源码中搜索 dispatch_queue_create 关键字,可以在 queue.c 中发现: dispatch_queue_tdispatch_queue_cre ...

  9. iOS之深入解析Block的底层原理

    一.block 本质 ① block 本质探究 定义一个 block.c 文件,键入以下代码: #include "stdio.h"int main() {void(^block) ...

  10. iOS之深入解析类加载的底层原理:分类如何加载到类以及分类和类的配合使用

    一.分类的本质 ① Xcode Documentation 通过 Xcode 文档搜索,在 Documentation 搜索 Category 关键字: 点击 Category ,如下: ② 通过 o ...

最新文章

  1. RGB与16进制颜色转换的原理
  2. Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?
  3. 高压放电与防静电塑料包装
  4. Java学习笔记(九)--数组及Arrays类
  5. 60秒计时器的仿真电路_物联网应用基于Arm微控制器的低功耗定时关机计时器
  6. C++Wiggle Sort摆动排序的实现算法(附完整源码)
  7. java poi 读取excel 编码_Java使用POI 读取和写入Excel指南
  8. Atom markdown .md 编写格式技巧
  9. 在线斯诺克html5,用HTML 5打造斯诺克桌球俱乐部
  10. 删除顺序表中重复元素,并按照原序输出
  11. c语言规定学号长度,c语言第1-9章基本概念练习题ans(最全).docx
  12. ubuntu 16.04 修改光标/鼠标大小
  13. Chisel Bootcamp安装说明
  14. 最近发现的一个c# winform的一个很好用的excel控件 reogrid控件
  15. mysql的month_MySQL month()函数
  16. 小程序动态tabBar菜单,根据条件渲染不同的tabBar
  17. Excel个人所得税简洁计算公式
  18. [质因数分解]樱花 洛谷P1445
  19. ChatGPT 之后,再玩玩 Stable-Diffusion
  20. Spring双生武魂之IOC

热门文章

  1. 【解决】瑞星杀毒软件无法卸载,rising进程无法关闭
  2. java 计算毫秒差值,关于时间的操作(Java版)——获取给定时间与目前系统时间的差值(以毫秒为单位)...
  3. 支付宝APP支付——支付流程说明及示例
  4. 设计分享|基于单片机的计数器设计(汇编)
  5. 用qq来搞定gmail,yahoo,hotmail,126 邮箱 POP3协议
  6. python keys方法_Robot Framework selenium操作键盘press keys方法详解(Python篇)
  7. 电脑win7做系统备份
  8. 网卡驱动的队列数据的接收e100
  9. 纯javascript模拟操作系统---jsOS
  10. python 统计英语文档词汇出现频率,以六级真题为例