iOS上KVO对比之 FBKVOController 与 GYD

2021-07-07  本文已影响0人  梨花树下好乘凉

前言

OC语言存在已久,其中有很多大家喜欢的设计,也有很多令人头疼的设计,更有让人又爱又恨的设计,KVO就是其中一项。好处自不用说,最令我遗憾的有2点:

  1. KVO的添加和触发并非线程安全。
  2. 同一个回调函数导致多个KVO之间可以互相干扰。
    为此除了自己简单封装了一下,也在网上搜索了一下别人的处理,其中知名度最大的无疑是 FBKVOController。那么就来对比一下吧。
    附上链接
    FBKVOController https://github.com/facebookarchive/KVOController
    GYDKeyValueObserver https://www.jianshu.com/p/a25260631d8f

线程安全

KVO是用来监听属性变化,那么第一个要考虑的线程安全问题便是:当我们在一个线程上添加和移除KVO的同时,这个属性在另一个线程上发生变化。以 FBKVOController 为例


线程安全问题

结果是不出意料的crash了,换成自己封装的 GYDKeyValueObserver 也是同样crash。
在网络上尝试搜索了一下,没有发现苹果API里有这处线程安全的参数配置,也没有发现有谁来解决,甚至没人把这当成一个问题,毕竟触发条件太苛刻。那么我们也不把这当成问题了。

KVO之间的干扰

这次先说 GYDKeyValueObserver
每一个KVO都用一个单独的对象来管理,彼此完全隔离。如下图:


GYDKeyValueObserver的效果

GYDKeyValueObserver的使用方式就如同最常见的变量赋值一样,怎么赋值就怎么监听,不赋值就不监听。使用KVO就如同使用一个最普通的OC对象。如下图:


GYDKeyValueObserver的使用

如果不知道如何使用OC对象,请自行学习。

再来看 FBKVOController
FBKVOController也是使用对象管理KVO。不仅如此,还可以使用同一个对象管理多个KVO,但有一个额外的要求,那就是不能监听同一个属性。如下图:

4.png

当第一个对象监听了key1之后,再想监听key1就要创建另一个对象,否则是无效的。也就是说,是否能使用一个对象管理多个KVO,取决于你是否对监听了哪个对象的哪些属性了如指掌,而这一点正是我们难以做到的。我们本是为了不用去考虑多个KVO之间的关联而封装了一个对象,如今为了判断多个KVO能否用同一个对象管理时,又需要去考虑这些KVO之间的关联,可以说是本末倒置了。

代码量

GYDKeyValueObserver 实现文件一共59行,除去开头注释,方法定义,非空判断等,逻辑代码在10行左右,简单到不好意思说成封装😂。也就是不知道发什么文章好了才拿出来凑数。
FBKVOController 实现文件一共676行,查看代码之后可以发现,大部分代码都是用来处理同一个对象管理多个KVO时的逻辑。

参数细节的区别

在参数细节方面 FBKVOController 做的比较全面,回调的方式有block和SEL可供选择,而 GYDKeyValueObserver 为了一切从简,只提供了block方式,需要使用者会使用block。
FBKVOController block的参数包括observer和object,从而免除这2个对象的weak处理。当然代价是需要提前设置好observer。而 GYDKeyValueObserver 为了一切从简,只提供了object参数(以前没有,最近刚加的),用户不用提前设置observer,但需要使用的时候,还需要做好weak处理。
FBKVOController 可以一次设置监听多个属性,GYDKeyValueObserver只能一次一个。但是平时写代码时就有人喜欢把多个代码耦合在一起,然后写一堆if else处理,也有人喜欢把代码分开,每个功能独立处理。我是后者,所以虽然FBKVOController接口更丰富,但这个功能对我无用。

总结

对于开发者来说,首先使用自己最了解的方式。如果都不了解,GYDKeyValueObserver是更好的选择,代码量小到一眼看透,傻瓜式用法一眼学会。

课后思考

可以说原本用10句代码已经能得到90分的成绩,FBKVOController又加入了100句代码将成绩降低到90分,是否值得?具体点就是,一个对象管理多个KVO,并且不能监听同一个key的必要性在哪里?那么来思考一个对象管理多个KVO的目的。
查看FBKVO内部代码发现其内部不但没有少建对象,反而因为要管理而建了更多的对象,并且还要加上线程安全处理,对于FBKVO本身来说没有任何好处。
而对于FBKVO外部的表现形式只有一个,就是少创建了变量,通常是成员变量,但我们通常对KVO的使用数量不多,能创建成员变量的情况下为每个KVO都创建一个成员变量并不影响什么,好处可以忽略。
那么还剩不能添加成员变量的情况。例如在其子类,分类,或者别的类里,为一个不想或不能修改代码的类扩展新的KVO处理,这时候自然是不能为其创建成员变量,但是同样的,这种情况下如果想利用一个已经提前定义好的成员变量(其指向的FBKVO对象),就要检查类本身的代码里,以及项目所有的代码里,有没有和我们一样利用这个变量又监听了同一个属性的。并且别人在开发代码时也要做同样的检查,不同分支合并时还要都再检查一遍。每次检查的结果如果是没有,那么恭喜你可以用了,而如果有,那么还是要重新创建一个变量。虽然出问题的概率也很小,但如果对自己的代码负责,我们就必须要做到。(手动表情:我要这封装有何用!)想到这里,我宁愿直接定义一个新变量。
思考的结论:在如此明显的情况下,FBKVOController依然这么做了,那只能说明FB内部还有一套与其配套的开发框架,例如充分利用KVO实现数据驱动一切的开发方式。
对于只有一个单独FBKVOController的外人,其内部多做的那些处理,只让那些性格随意不拘小节的人可以轻微的偷懒,对于严谨负责的人来说只是累赘。

上一篇下一篇

猜你喜欢

热点阅读