iOS 手动实现KVO / iOS KVO底层原理
手动实现KVO/KVO底层原理
git demo地址
内部实现原理
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底层原理相关推荐
- iOS之深入解析类加载的底层原理:类如何加载到内存中
一.App 启动与 dylb 加载 App 启动会由 libdyld.dylib 库先于 main 函数调用 start,执行 _dyld_start 方法,然后运用汇编实现调用 dyldbootst ...
- iOS之深入解析KVC的底层原理和自定义KVC的实现
一.KVC 简介 ① 定义 KVC 是 Key-Value Coding 的简称,中文译义为键值编码. KVC 是指 iOS 的开发中,可以允许开发者通过 Key 名直接访问对象的属性,或者给对象的属 ...
- iOS之深入解析Runloop的底层原理
一.Runloop 简介 ① 什么是 Runloop ? RunLoop 是事件接收和分发机制的一个实现,是线程相关的基础框架的一部分,一个 RunLoop 就是一个事件处理的循环,用来不停的调度工作 ...
- iOS之深入解析AFNetworking的底层原理
AFNetworking 简介 一.AFNetworking 版本 AFNetworking 是 iOS 最常用的网络框架,虽然系统也有 NSURLSession ,但是一般不会直接用它.AFNetw ...
- iOS之深入解析malloc的底层原理
一.前言 iOS 在创建对象的时候,alloc 方法有三个核心部分:cls->instanceSize(计算需要开辟内存的大小),calloc(开辟内存空间),obj->initlnsta ...
- iOS之深入解析渲染的底层原理
一.计算机渲染原理 ① CPU 与 GPU 的架构 对于现代计算机系统,简单来说可以大概视作三层架构:硬件.操作系统与进程.对于移动端来说,进程就是 App,而 CPU 与 GPU 是硬件层面的重要组 ...
- iOS之深入解析“锁”的底层原理
一.OSSpinLock(自旋锁) 自从 OSSpinLock 出现安全问题,在 iOS10 之后就被 Apple 废弃.自旋锁之所以不安全,是因为获取锁后,线程会一直处于忙等待,造成了任务的优先级反 ...
- iOS之深入解析GCD的底层原理
一.队列 ① 队列实现源码分析 在源码中搜索 dispatch_queue_create 关键字,可以在 queue.c 中发现: dispatch_queue_tdispatch_queue_cre ...
- iOS之深入解析Block的底层原理
一.block 本质 ① block 本质探究 定义一个 block.c 文件,键入以下代码: #include "stdio.h"int main() {void(^block) ...
- iOS之深入解析类加载的底层原理:分类如何加载到类以及分类和类的配合使用
一.分类的本质 ① Xcode Documentation 通过 Xcode 文档搜索,在 Documentation 搜索 Category 关键字: 点击 Category ,如下: ② 通过 o ...
最新文章
- RGB与16进制颜色转换的原理
- Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?
- 高压放电与防静电塑料包装
- Java学习笔记(九)--数组及Arrays类
- 60秒计时器的仿真电路_物联网应用基于Arm微控制器的低功耗定时关机计时器
- C++Wiggle Sort摆动排序的实现算法(附完整源码)
- java poi 读取excel 编码_Java使用POI 读取和写入Excel指南
- Atom markdown .md 编写格式技巧
- 在线斯诺克html5,用HTML 5打造斯诺克桌球俱乐部
- 删除顺序表中重复元素,并按照原序输出
- c语言规定学号长度,c语言第1-9章基本概念练习题ans(最全).docx
- ubuntu 16.04 修改光标/鼠标大小
- Chisel Bootcamp安装说明
- 最近发现的一个c# winform的一个很好用的excel控件 reogrid控件
- mysql的month_MySQL month()函数
- 小程序动态tabBar菜单,根据条件渲染不同的tabBar
- Excel个人所得税简洁计算公式
- [质因数分解]樱花 洛谷P1445
- ChatGPT 之后,再玩玩 Stable-Diffusion
- Spring双生武魂之IOC
热门文章
- 【解决】瑞星杀毒软件无法卸载,rising进程无法关闭
- java 计算毫秒差值,关于时间的操作(Java版)——获取给定时间与目前系统时间的差值(以毫秒为单位)...
- 支付宝APP支付——支付流程说明及示例
- 设计分享|基于单片机的计数器设计(汇编)
- 用qq来搞定gmail,yahoo,hotmail,126 邮箱 POP3协议
- python keys方法_Robot Framework selenium操作键盘press keys方法详解(Python篇)
- 电脑win7做系统备份
- 网卡驱动的队列数据的接收e100
- 纯javascript模拟操作系统---jsOS
- python 统计英语文档词汇出现频率,以六级真题为例