当前位置: 首页 > ai >正文

KVC与KVO

一、KVO

KVO全称Key-Value-Observing,作用是对象监听另一个对象特定的属性的改变,并在改变时接收到事件,举例如下:

如果person对象的name属性改变了,就会调用监听者BaseController的observeValueForKeyPath方法。

1.1 KVO基本使用

1)通过addObserver:forKeyPath:options:context:方法注册观察者,观察者可以接收keyPath属性的变化事件。

2)在观察者中实现observeValueForKeyPath:ofObject:change:context:方法,当keyPath属性发生改变后,KVO会回调这个方法来通知观察者。

3)当观察者不需要监听时,可以调用removeObserver:forKeyPath:方法将KVO移除。需要注意的是,调用removeObserver需要在观察者消失之前,否则会导致Crash。

1.2 KVO实现原理

1)为原生的类生成一个名为NSKVONotifying_***的子类,子类重写了属性的set方法,里面调用了willChangeValueForKey和didChangeValueForKey,还重写了class方法,让其看着更像原类,然后obj的isa指向了监听类。set方法大致就如下图所示:

2)在调用obj的属性set方法时,在didChangeValueForKey:方法中自动调用了所有Observe的observeValueForKeyPath:ofObject:change:context: 方法。这样就实现了属性监听。

1.2.1 KVO中间类验证:

在添加监听的addObserver:forKeyPath:options:context:方法下面得到obj的Class类和父类,代码如下:

打印效果如下:

1.2.2 验证 didChangeValueForKey: 内部调用监听方法

在Person类中,重写重写 willChangeValueForKey: 和 didChangeValueForKey: 方法,模拟它们的实现。具体代码如下:

打印效果如下:

二、bk中的KVO

BlockKit第三方库中有个NSObject_BKBlockObservation.m的文件,提供了很方便加入Observe功能的一些API,比如- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath task:(void (^)(id target))task方法,主要解决传统KVO的以下几个问题:

1)KVO使用不当很容易导致崩溃,比如重复add和remove、Observer被释放、keyPath传错等。

2)写起来太麻烦,每个Observe都得重写observeValueForKeyPath:ofObject:change:context:

2.1 bk的KVO的原理

在添加Observer时,最终都会调入接口

- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths

identifier:(NSString *)identifier

options:(NSKeyValueObservingOptions)options

context:(BKObserverContext)context

task:(id)task

传参说明如下:

参数名称(类型)

备注

keypaths(NSArray *)

所有被监听的属性

identifier(NSString *)

这次添加监听的唯一ID

options(NSKeyValueObservingOptions)

监听task中需要回传的参数

context(BKObserverContext)

task需要回传什么

typedef NS_ENUM(int, BKObserverContext) {
//只回传被监听的对象BKObserverContextKey,
//Change+对象	BKObserverContextKeyWithChange,
//keyPath+对象BKObserverContextManyKeys,
//Change+keyPath+对象	BKObserverContextManyKeysWithChange
};

task(id)

具体的block

具体代码如下:

2.2 _BKObserver对象

_BKObserver其实就是一个监听对象,是task和被监听对象的中介者。实现了

- (void)observeValueForKeyPath:(NSString *)keyPath

ofObject:(id)object

change:(NSDictionary *)change

context:(void *)context

来具体调用task。

2.2.1 _BKObserver的构造方法:

2.2.2 _BKObserver开始监听startObservingWithOptions

startObservingWithOptions是开启监听的入口,对被监听对象添加所有keyPath的监听,具体代码如下:

2.2.3 _BKObserver中的监听方法

_BKObserver类的监听方法,当keyPath发生变化时,调用task回调,具体代码如下:

2.3 dealloc中的删除所有监听

入口函数是bk_removeAllBlockObservers,具体代码如下:

stopObserving函数又调用了_stopObservingLocked,作用就是将被监听对象添加的所有keyPath的监听全部remove。具体代码如下:

三、KVC

KVC全称是Key Value Coding,KVC是一种利用字符串来访问对象的属性方法或者成员变量机制。原理主要依赖与setter和getter方法。另外accessInstanceVariablesDirectly表示在搜索的过程中是否允许读取实例变量的值,默认为YES。

3.1 赋值原理

以setValue:forKey:为例,运行步骤如下:

1)以set<Key>、_set<Key>的顺序查找对应命名的setter方法,如果找到的话,调用这个方法并将值传进去。

2)如果没有找到setter方法,但是accessInstanceVariablesDirectly类方法返回YES,则按_<Key>、_is<Key>、<Key>、is<Key>的顺序查找一个对应的实例变量,如果找到那么将value赋值。

3)如果没有找到setter方法或实例变量,则调用setValue:forUndefinedKey:方法,默认是抛出异常,也可进行重写。

流程图如下:

3.2 取值原理

以valueForKey:为例,运行步骤如下:

1)以get<Key>、<Key>、is<Key>、_<Key>的顺序搜索方法,如果有,直接调用。

2)如果没有getter方法,但是accessInstanceVariablesDirectly类方法返回YES,那么按照_<Key>、_is<Key>、<Key>、is<Key>的顺序查找实例变量。

3)如果返回值是一个对象指针,则直接返回。如果是一个基础数据类型,而且Number类型支持,那么被Number封装再返回,如果不被支持,那么NSValue封装并返回。

4)在上述情况都失败的情况下调用valueForUndefinedKey:方法,默认是抛出异常,也可进行重写。

流程图如下:

http://www.xdnf.cn/news/12614.html

相关文章:

  • Scade 语言概念 - 方程(equation)
  • DenseNet算法 实现乳腺癌识别
  • 游戏(game)
  • Go 语言 := 运算符详解(短变量声明)
  • Sum of Prod of Mod of Linear_abc402G
  • python的numpy的MKL加速
  • C# 类和继承(扩展方法)
  • 高精度算法--四则运算模板(附例题)
  • Nginx Stream 层连接数限流实战ngx_stream_limit_conn_module
  • SpringBoot十二、SpringBoot系列web篇之过滤器Filte详解
  • 2.3 VS2019 简单使用
  • LLM 笔记:Speculative Decoding 投机采样
  • HTML 面试题错题总结与解析
  • vue3 icon 图标 使用方法
  • VScode打开后一直显示正在重新激活终端 问题的解决方法
  • Modbus RTU与Modbus TCP详解指南
  • 安卓基础(编译.Class)
  • MCP详解及协议的使用(python版本和Node版本)
  • 【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
  • cv2.stereoRectify中R1, R2, P1, P2, Q中每一个分量的物理意义
  • grep、wc 与管道符快速上手指南
  • Linux八股【3】-----系统框架概述
  • SpringBoot项目接口集中测试方法及实现
  • 2.4 vcpkg 使用
  • Vue中渲染函数的使用
  • Promise深入理解
  • 【靶场】yzmcms5.3.0 SSRF漏洞
  • 分享一道力扣
  • 又是一年高考季
  • mariadb5.5.56在centos7.6环境安装