KVO自定义

1.jpeg

很长一段时间没有更新文章了,主要是自己在边当码农边学习充电(学习不写文章就是偷懒)。最近系统的学了一下Flutter,两年前搞过原生和Flutter混编,不过那时候只是埋头完成工作也没咋上心,这次系统的过了一遍也简单的了解了下Flutter的一些底层实现原理。这个后期也会选择性的出一些文章比如Flutter“异步”"多线程"“增量渲染”“底层原理”等。接下来进入今天的主题。

KVO官方实现的分析

既然在上一篇文章我们探究了系统KVO 的底层实现原理,那我们自己实现也难逃窠臼,照着搞一套呗。所以完全可以仿照系统的API,不管三七二十一搞一套对外的API先。看 图1 系统的API

1.png

系统的KVONSObject的一个分类,然后实现了三个对外的API。分别是添加观察者API移除观察者API观察者值变化接收通知API。这三个API 我们也是熟悉的不能再熟悉的了。不过我们可以发现,系统对这些API的实现还不是在一个分类里。乃是分了两个分类。那我们不需要这么麻烦。我们都放在一个分类里搞定就好。

KVO仿官方自定义实现

1,创建分类仿照系统的API 实现自己的API

我们创建NSObject的分类ZYKVO。并且仿照系统的定义三个对外API 如图2:

2.png
2,根据上节课的底层原理实现自己的API
(1)添加观察者API 实现

我们先来捋一捋添加观察者的步骤:
1: 首先判断进来的观察值是否是实例变量(因为实例变量不在观察范围我们只观察属性)
2: 动态生成子类 并且添加类方法setter方法(完全仿照系统的做法)
3: 改变isa的指向 : 指向我们动态创建的子类ZYKVONotifying_ZYPerson (这个子类的名字也是仿照系统的取名规则)
4: 保存观察者 因为我们在最后还要给观察者发送通知 告诉观察者 值的变化 (这里采用 关联对象 方法保存 因为我们是NSObject 的一个分类)

接下来我们按照上面的步骤来一步一步实现:

1:判断是否是实例变量:

3.png

2: 动态生成子类:

4.png

IMP的自定义方法 zy_classzy_setter:

5.png

到这里我们应该可以拦截到观察的属性值了。我们来验证下:

6.png
7.png
8.png

3: 修改isa的指向,讲父类的isa指向动态创建的子类:

6.png

4: 保存观察者对象和信息:

注意:因为我们的观察者肯定不止一个,所以我们要留有后路保存的必须是一个数组类型的;既然是多个观察者那肯定有多个keyPath 多个option 多个context;那我们不如创建一个信息类来保存这些关联信息,然后我们只需要保存一个个的信息对象数组里,我们就在本文件里创建一个信息类ZYKVOInfo 如图:

7.png
8.png

5: 验证:

下面我们就需要去我们自己定义的zy_setter方法里去实现剩余的步骤,即发消息给父类调用父类的setter方法进行赋值发通知到观察者ViewController告诉观察者已经观察到改变了并且调用观察者实现的观察方法

10.png
11.png
12.png

到这里已经完成一小半了,已经可以成功拦截属性赋值到我们动态生成的子类zy_setter方法里了。

接下来我们就要去实现剩下的步骤了如:实现zy_setter方法去给父类发消息改变父类属性的值,并且发通知给观察者告知KVO的值的变化情况。下面我们就来实现这个方法:

13.png
(2)通知观察者API 实现
14.png
15.png
16.png

验证:

17.png

至此,我们的自定义ZYKVO已经实现了基本功能了,还差最后一步的移除操作。

(3)移除观察者API 实现

移除观察者我们在上一篇文章对KVO分析的时候发现它只是把isa转回到了父类,但是新创建的子类并没有直接删除。我们也只需要如此操作就可以:

18.png

到这里我们基本上实现了基础的自定义KVO功能。不过其中还有很多可以优化的地方,比如在zy_setter方法里开始我们应该判断一下KVO自动开关的问题,因为开关默认是打开的,如果手动关闭则就要做处理,不去进行下面的步骤发消息了;或者说我们是否可以考虑观察者的自动释放功能?是否可以考虑利用函数式编程思想来修改KVO?接下来我们实现下:

KVO函数编程思想优化实现

在前面“仿照官方KVO”的方式自定义实现了基本功能,但是我们发现我们使用的时候观察对象都需要去实现一个观察方法,并且在这个方法里如果有观察多个对象还要进行各种判断来处理业务逻辑。这很麻烦。那我们是否可以考虑利用函数式编程思想来解决这种问题呢?答案当然是可以的,我们在上面自定义的基础上来实现下:

1,根据前面实现的信息保存类ZYKVOInfo进行改造
19.png
20.png
2,根据前面实现的添加观察者API进行改造
21.png
22.png

3, 根据通知观察者API 进行改造

23.png
24.png

验证:

25.png

这样我们就简单的改造了前面定义的KVO 改成了函数式的方法调用和封装。

KVO自动移除观察者优化实现

我们使用观察者都有一个共识就是及时移除观察者,在上一篇文章KVO分析中也有验证不移除的后果。但是每次都要手动移除难免略显麻烦,那我们不禁想是否有办法让它自动移除呢?就如我们借用函数式编程思想来解决添加观察者和监听方法分离的方法呢?我们一起探究下。

思路探究:
1,首先我们一般移除某个对象的观察者方法都是会放在其dealloc方法里。如果我们要实现自动释放观察者,那我们是否可以通过监听到对象的释放调用dealloc方法的时候就自动调用我们的KVO释放方法呢?
2,我们知道不可能用通知啊之类的因为我们是要覆盖所有监听对象的dealloc方法。那我们是否可以在我们自定义的ZYKVO类对外界观察者的dealloc进行某些处理以至于当外界的观察者的dealloc调用的时候我们就能知道呢?
3,首先想到的就是我们常用的黑魔法-方法交换
4,我们以前用方法交换都是在load方法里就直接交换了,但是这里我们仔细思考后发现不能在load方法里实现。原因是我们的ZYKVONSObject类的分类,如果在load方法里拦截和交换就会把所有系统和我们代码所有的类的dealloc方法都被拦截和交换一次。而我们只需要针对观察者的类进行方法交换。
5,那我们是否可以考虑去观察者添加方法里去做交换处理?
6,在观察者添加方法里的去处理的时候我们发现我们还发现有可能对同一个观察者交换多次的问题。
7,在观察者添加方法里交换方法也需要去判断观察者类是否实现了dealloc方法,如果没有实现还要去动态添加然后进行交换。
8,那我们还有其他办法么?我们想到这里不禁想到我们实现的zy_setter方法。我们在前一篇文章中探究KVO的底层原理释放那段的时候发现,当观察者类在走dealloc方法的时候isa的指向还是动态生成的子类,也就是说此时调用的dealloc是动态生成的子类的,那我们直接对动态生成的子类多添加一个dealloc方法不就相当于在观察者走dealloc方法的时候就走了动态子类的dealloc方法么?我们在动态子类添加的dealloc方法去直接释放不就好了么?想到就做,我们试试。

KVO自动释放实现
26.png

验证:

27.png
28.png

至此,文章的主要内容已经全部完成了,基本也实现了我们自己的想法。不过内部还有很多细节和判断其实是需要处理的。不过我这篇文章主要是根据上一篇文章的内容来简单实现一下主要流程。有兴趣的可以去做更多的完善,比如在zy_setter方法里开始我们应该判断一下KVO自动开关的问题,因为开关默认是打开的,如果手动关闭则就要做处理,不去进行下面的步骤发消息了,自动释放KVO的方法还有很多细节值得去优化例如在zy_dealloc调用完通知父类调用dealloc方法等等细节问题。

论到KVO 的自定义以及KVO自动释放的问题,其实有一个框架非常值得我们去学习,就是Facebook开发的框架KVOController

遇事不决,可问春风。站在巨人的肩膀上学习,如有疏忽或者错误的地方还请多多指教。谢谢!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容