KVO的用法、底层实现原理
KVO的用法
KVO也就是key-value-observing(即键值观察),利用一个key来找到某个属性并监听其值得改变。用法如下:
- 添加观察者
- 在观察者中实现监听方法,observeValueForKeyPath: ofObject: change: context:(通过查阅文档可以知道,绝大多数对象都有这个方法,因为这个方法属于NSObject)
- 移除观察者
//让对象b监听对象a的name属性
//options属性可以选择是哪个
/* NSKeyValueObservingOptionNew =0x01, 新值
* NSKeyValueObservingOptionOld =0x02, 旧值
*/
[a addObserver:b forKeyPath:@"name"options:kNilOptionscontext:nil];
a.name = @"zzz";
#pragma mark - 实现KVO回调方法
/* * 当对象的属性发生改变会调用该方法
* @param keyPath 监听的属性
* @param object 监听的对象
* @param change 新值和旧值
* @param context 额外的数据
*/
- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)objectchange:(NSDictionary<NSString *,id>*)change context:(void *)context{
NSLog(@"%@的值改变了,",keyPath);
NSLog(@"change:%@", change);
}
//最后不要忘记了,去移除observer
- (void)dealloc{
[a removeObserver:b forKeyPath:@"name"];
}
KVO键值观察者底层解析
涉及到了runtime,关于isa指针
手动实现键值观察(代码示例)
被观察的对象Target(重写setter/getter方法)
Target.h
@interface Target : NSObject
{
int age;
}
// for manual KVO
- age- (int) age;
- (void) setAge:(int)theAge;
@end
Target.m
@implementation Target
- (id) init{
self = [super init];
if (nil != self) {
age = ;
}
return self;
}
// for manual KVO - age
- (int) age{
return age;
}
- (void) setAge:(int)theAge{
[self willChangeValueForKey:@"age"];
age = theAge;
[self didChangeValueForKey:@"age"];
}
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"age"]) {
return NO;
}
return [super automaticallyNotifiesObserversForKey:key];
}
@end
首先,需要手动实现属性的 setter 方法,并在设置操作的前后分别调用 willChangeValueForKey: 和 didChangeValueForKey方法,这两个方法用于通知系统该 key 的属性值即将和已经变更了;
其次,要实现类方法 automaticallyNotifiesObserversForKey,并在其中设置对该 key 不自动发送通知(返回 NO 即可)。这里要注意,对其它非手动实现的 key,要转交给 super 来处理。
实现原理
KVO的实现是基于runtime运行时的,下面就来详细介绍一下原理:还是这张图:
- 当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
- 派生类在被重写的 setter 方法中实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。
- 同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。
KVO与Notification之间的区别:
notification是需要一个发送notification的对象,一般是notificationCenter,来通知观察者。
KVO是直接通知到观察对象,并且逻辑非常清晰,实现步骤简单。
KVO的用法、底层实现原理的更多相关文章
- iOS weak底层实现原理
今年年底做了很多决定,离开工作三年的深圳,来到了上海,发现深圳和上海在苹果这方面还是差距有点大的,上海的市场8成使用swift编程,而深圳8成的使用OC,这点还是比较让准备来上海打拼的苹果工程师有点小 ...
- Kubernetes(k8s)底层网络原理刨析
目录 1 典型的数据传输流程图 2 3种ip说明 3 Docker0网桥和flannel网络方案 4 Service和DNS 4.1 service 4.2 DNS 5 外部访问集群 5.1 外部访问 ...
- Java 并发系列之二:java 并发机制的底层实现原理
1. 处理器实现原子操作 2. volatile /** 补充: 主要作用:内存可见性,是变量在多个线程中可见,修饰变量,解决一写多读的问题. 轻量级的synchronized,不会造成阻塞.性能比s ...
- 老生常谈系列之Aop--CGLIB动态代理的底层实现原理
老生常谈系列之Aop--CGLIB动态代理的底层实现原理 前言 上一篇老生常谈系列之Aop--JDK动态代理的底层实现原理简单讲解了JDK动态代理的实现,动态代理常用实现里面的双子星还有另一位--CG ...
- PHP底层工作原理
最近搭建服务器,突然感觉lamp之间到底是怎么工作的,或者是怎么联系起来?平时只是写程序,重来没有思考过他们之间的工作原理: PHP底层工作原理 图1 php结构 从图上可以看出,php从下到上是一个 ...
- Java并发之底层实现原理学习笔记
本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...
- spirng底层实现原理
什么是框架?框架解决的是什么问题? 编程有一个准则,Don't Repeat Yourself(不要重复你的代码),所以我们会将重复的代码抽取出来,封装到方法中:如果封装的方法过多,将将这些方法封装成 ...
- 《Java并发编程的艺术》Java并发机制的底层实现原理(二)
Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...
- Spring(二)IOC底层实现原理
IOC原理 将对象创建交给Spring去管理. 实现IOC的两种方式 IOC配置文件的方式 IOC注解的方式 IOC底层实现原理 底层实现使用的技术 1.1 xml配置文件 1.2 dom4j解析xm ...
随机推荐
- mysql 5.7.12----bin/mysqld --initialize --user=mysql出错
我最近在安装mysql 5.7.12,本来之前安装mysql 5.7.11时用命令 bin/mysqld --initialize --user=mysql 可以很好的初始化,但是用在5.7.12版本 ...
- 安装 Windows SDK for Windows 7 时遇到的一个问题及解决办法
最近试着用 VS2010 + Qt 开发程序,发现 VS2010 里面没有提供单独的调试器 cdb,这样用 Qt Creator 时就无法设置断点调试,很不方便.想起 Windows SDK for ...
- Qt 自定义事件的实现
初学Qt,用了Qt自带的事件,然后想怎么才能定义自己的事件呢?又如何使用自定义事件呢?看了篇文章,说先要子类化QEvent,然后定义自己的QEvent::Type,然后重写QWidget::event ...
- php 删除指定文件 glob,unlink
我用最简单的语句写了一个php删除指定文件 因为一直越级挑战thinkphp,所以突然面对php有点无所适从了... 首先,php的运行机制,是一个语法就搞定了,还是非要用数据库,还是post什么的. ...
- 第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型
第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型 Hash操作,redis中Hash在内存中的存储格式如下图: hset(name, key, value)name ...
- 在安装ZooKeeper之前,请确保你的系统是在以下任一操作系统上运行
在安装ZooKeeper之前,请确保你的系统是在以下任一操作系统上运行: 任意Linux OS - 支持开发和部署.适合演示应用程序. Windows OS - 仅支持开发. Mac OS - 仅支持 ...
- yasm开源汇编器分析
https://www.google.com.hk/search?q=yasm&oq=yasm&aqs=chrome..69i57&sourceid=chrome&es ...
- Oracle-05-SQL语句概述、分类&SQL*PLUS概述(初识insert,desc,list,r,del,a,c,n等命令)
一.SQL语句概述 (1)SQL全程是"结构化查询语言(Structured Query Language)". SQL是大多数主流数据库系统採用的标准查询语言. (2)SQL语句 ...
- 【Java集合的详细研究3】Arrays类常用方法
1.void sort(array)对数组array的元素进行升序排序 2.int binarySearch(array,val):查询元素值val在数组array中的下标 3.String toSt ...
- php字符串算术表达式计算
$aa = "{1}*{2}-{3}"; $farr = array('/\{1\}/','/\{2\}/','/\{3\}/'); $tarr = array(3,4,10); ...