闲来无事做,不如学学习
平常项目场景中,肯定会出现这样一个场景,多个网络请求同时进行,但却要将多个网络请求的结果同时进行处理
网上的解决办法很多,我之前项目中采用的就是GCD的dispatch_group_notify来做的,但是这样有一个弊端,代码写起来杂乱,看起来很不美观,管理不方便,不容易维护
后来学习了Reactive Cocoa框架(不得不说这个框架是十分nb啊)
其中有一个是专门用来解决这个问题的,就是rac_liftSelector,有兴趣的朋友可以去网上搜一搜rac_liftSelector这个方法,就会发现真的很方便,也很强大
那既然RAC已经完美解决了这个问题,为什么我还要说一说呢,因为我们项目没用rac啊,哈哈哈
项目最开始就是按照MVC搭建,如果换MVVM+RAC 需要把整个项目重新写一遍了。。。(本人也是比较懒),所以决定,秉承着懒人原则,自己动手造一个轮子
先说明RAC处理办法:
RAC 在NSObject的一个信号Category中写了rac_liftSelector,而rac整套框架都基于一个概念,信号(RACSingle)
可以理解为,一个操作就是一个信号,未被触发的操作为冷信号,已触发的操作为热信号
Github Demo地址: https://github.com/985589477/Signal
未解决bugList(后面会一一解决,解决后会添加删除线):
NSObject的分类中添加KVO会导致bug,假如其他使用该方法继承NSObject的类实现了KVO的observeValueForKeyPath,会导致NSObject的分类中的observeValueForKeyPath不执行。
需要优化列表
对Signal进行功能扩展,下一功能: 实现异步顺序执行队列,类似js中的Promise(仅仅是类似),可以不断在后面追加处理结果的block
添加信号组的销毁功能,防止使用时某个信号忘记使用sendSignal: 而导致无法dealloc,同时也可以解决在控制器已经pop了,还有残留信号响应,直到响应结束才dealloc的问题,只要调用信号组销毁,则该组内的所有子信号都会进行释放
代码结构可以添加信号组(SingleGroup)优化
命名问题: Signal(信号),我居然写成了Single(???) ,what?,我不是单身狗啊,为什么会用Single???
使用时需要注意的地方
每一个Signal的block中都需要进行[subscriber sendSingle:(nil)||(结果)]; 必须调用该sendSingle函数,否则最后的通知selector无法执行
2018 - 11 - 19日更新(当前最新) (下面有以前版本,但可能存在部分bug,最新的是修改过的)
此版本主要优化:
添加信号组的销毁功能,防止使用时某个信号忘记使用sendSignal: 而导致无法dealloc,同时也可以解决在控制器已经pop了,还有残留信号响应,直到响应结束才dealloc的问题,只要调用信号组销毁,则该组内的所有子信号都会进行释放
本次迭代结束后基本可以达到一个正常使用的框架而不会担心有bug问题
之前无法sendSignal而出现意外导致的无法释放,可能出现在,网络请求超时,等待时间过长,而这个时候控制器已被销毁,而网络请求只有走完了才会sendSignal,最终导致的就是控制器释放问题,
那么我们需要提供函数方法,来解决这个问题,正常大部分人都喜欢把操作放入dealloc中,但是这种需要视情况而定,因为一些新手同学,认为放入dealloc就一定可以释放,比如NSTimer,把timer的停止以及赋值为nil都放在了dealloc中,dealloc是释放了才会走,那不释放就不会走,而把timer处理掉就是为了走dealloc,所以陷入了一个死循环,所以我们这个函数需要写在viewDidDisappear中,也就是视图已经消失的回调方法,而我们发送信号组是与self进行绑定的,那么我们的释放信号也应该与self进行绑定,那么我们在NSObject+Signal.h中提供一个方法
- (void)destructionSignal;
提供了这个方法之后我们需思考之后的逻辑,我们的单个信号是存放于信号组内的,而self绑定了我们的信号组,所以,我需要给信号组也提供一个销毁的函数方法
我们定义一个GroupSubscriber Protocol ,提供方法- (void)destructionSignal;并让SignalGroup遵守该协议
那么我们方法已经定义好了,下面需要考虑实现了,
我们在NSObject+Signal.m中使用了NSMutableDictionary中保存了信号组,而信号组也有销毁信号的方法,所以我们只需要遍历获取所有groupSignal,并且调用释放方法,最终将NSMutableDictionary中所有的数据进行移除
group中该如何做呢?
其实我们只需要对每个信号[signal sendSignal:nil] ;就可以了,这样就解决了信号的释放问题
这样难道就没有bug或者准确来说,没有体验不好的地方了嘛?当然有,我们的信号每个都send nil,最终还是会调用最终的回调方法,可以当我们进行释放的时候,这一步是多余的操作,可以不用的,而且假如用户传入了必传的数据,在使用的时候没有进行判空处理,那么将会引起崩溃
所以我们接下来要做的就是,收到释放的信号,就让他不走最终的通知回调,我们在GroupSignal中提供一个isDestruction属性,在调用Group的destructionSignal时,我们将属性设置为true,那么在KVO的监听回调方法observeValueForKeyPath: ofObject: change: context:中,进行一个判断过滤掉就OK了
2018 - 11 - 16日更新
此版本主要优化:
代码结构添加信号组概念
命名问题: Signal(信号),我居然写成了Single(???) ,what?,我不是单身狗啊,为什么会用Single???
Bug修复:
NSObject的分类中添加KVO会导致bug,假如其他使用该方法继承NSObject的类实现了KVO的observeValueForKeyPath,会导致NSObject的分类中的observeValueForKeyPath不执行。
本次修改也是大幅度修改
1、首先移除了WaitSignal类
2、建立SignalGroup类
3、将KVO的响应者转移为SignalGroup,解决分类中实现KVO导致的bug
Signal与Subscriber类都没有进行修改,对外暴露的使用方式也没有修改
首先我们来看一下NSObject+Signal中的实现
可以看到,代码少了很多,是因为将Observer转移给了signalGroup
我们从上到下看一遍
首先利用提供给外界的方法来调用,初始化一个SignalGroup对象,因为我们要对每一组信号做一个绑定,所以在group中提供了一个identifier的属性,并且存储在signalsDictionary中
其次调用 signalWithGroup: 方法,对每一个信号添加观察并且执行代码块中的代码
接下来最重要的代码就是SignalGroup了
可以看到,最上面的构造方法中创建了一个group对象并对其赋值,其次为该group分配了一个identifier,然后在singleArray的set方法中,将该信号组内的信号的identifier与信号组的identifier进行绑定,最终将observeValueForKeyPath实现即可
需要注意的是:
1、我们需要在每一个信号执行结束后对其移除观察者
2、我们需要在一个信号组执行结束后取消其保存,否则无法dealloc
2018 - 11 - 13日更新
本修改基于上一版本,如果有疑问可以参考上版本代码
首先我说明我上个版本的bug以及问题
1、代码没有rac简洁
2、SingleManager如果不强引用则会导致crash
3、若该组方法未执行完毕,又重新reload一次会导致SingleManager强引用失效,造成crash
4、即使解决了SingleManager的强引用,也会导致在多次执行通知回调触发方法结束后回调的数据错乱,很严重的问题
基于以上问题,对代码进行修改及重构
还是本着一个原则,参考RAC,抱大腿
首先忽略第一个问题,看第二个问题
SingleManager不强引用会导致crash,RAC做法:写在NSObject的分类中
写在Category中很好的解决了这个问题,为什么?因为当前对象就是控制器,控制器不释放,他就不会释放,也就不会造成野指针崩溃(之前我就忽略了这个细节,果然需要打磨)
第三个问题&&第四个问题
其实解决了第二个问题就相当于解决了第三个问题,但是他们是一个连锁反应,我需要把遇到的问题提出来,大家可以共同参考进步
而第四个问题就是我在解决了SingleManager强引用问题的同时导致的,同时调用两次,会发现,回调数据错乱,因为代码没有做数据绑定
有了以上的解决方案,那么来解决一下,代码变动较大,直接从头捋清思路
创建 NSObject+Single的Category、创建Single的Class、创建Subscriber的Protocol、创建WaitSingle的Class(处理数据信号绑定)
首先创建Subscriber的Protocol ,为其添加方法 - (void)sendSingle:(id)result;
此处需要有一个修改 将 NS_ASSUME_NONNULL_BEGIN 与NS_ASSUME_NONNULL_END 删除,否则在该方法传入nil的时候有一个黄色警告,比较烦
然后我们来看一下Single类中的修改
我们首先把之前SingleManager中的创建信号任务的方法转移到Single中,并且修改了一下命名,以及添加了Single的标示符号
以下是.h 和 .m
可以看到,只把之前的SingleManager中创建信号方法转移到了Single中,实现没有改变
接下来就是最重要的了
将SingleManager中的触发方法放入NSObject+Single中
并且创建WaitSingle用来做数据绑定模型类
将SingleManager.m中的属性全都转移到该类
其次提供方法为Single创建组合标示identifier
然后修改报错部分代码进行替换,以下是实现
这样我们就完成了对代码的修改,以下是代码的使用方式
2018 - 11 - 12 日初稿
基于该rac原理,对信号进行思考,无非就是多个请求结束后,触发某个操作
首先想到的是什么,KVO啊,每一个操作都为一个信号,我想要监听其改变,那么我利用KVO进行观察就OK啦
其次思考,rac中利用sendNext来处理结果,那么我也提供一个函数方法来处理结果
然后开始动手
首先创建2个类: Single 、 SingleManager
Single主要就是作为一个信号,而SingleManager就是用来管理信号的
既然SingleManager是用来管理信号的,那么好,我需要向singleManager添加任务(一次网络请求,或者是一些代码等被我统称为任务 task),其次,我需要提供多个方法执行结束后所触发的方法(其实就好像一个UIButton 对其addTarget,只不过换了一个形式)
那么Single类是用来操作信号的,所以我需要对Single类添加存储外界传入的task,以及提供一个id 类型的result来调用以及一个方法 发送信号,将冷信号设置为热信号
下面是我的Single.h定义
下面是我的SingleManager.h的定义
这里我采用block的方式进行添加任务
使用- (LXSingle*)singleAddTask:(void(^)(LXSingle*lx_single))task;来进行定义任务
使用 - (void)singleWithSelector:(NSArray
使用代码如下
注意:singleManager一定要使用全局变量进行强引用,否则Single中sendSingle会因为局部变量的释放而导致Crash
-----------------分割线(以上是初稿)--------------
最后说一句,喜欢的话给个小红心,有问题可以评论提问
爱你们呦 尊重原创,转载请著名作者以及地址
https://www.jianshu.com/writer#/notebooks/11755935/notes/36638571