现在RAC大行其道,对其讲解的博客也多不胜数,稍微有点经验的估计也已经对这个爽到不要不要的框架运用自如了,真正沉下来研究其实现原理的估计也不在少数,这里仅仅是记录一下本身的分析理解,更是在写这篇博客的过程当中深化本身对RAC的认知,可能就是想到哪写到哪,各位朋友能从其中学到东西是最好了,要是感受没什么干货也别对小弟拍砖啊!数组
以下图:框架
完成一个信号的生命周期分为四步:异步
下面每一步咱们细细道来:学习
由上面的 信号类使用图
可知,建立信号类方法中传入了一个返回值是RACDisposable
类型,参数是遵照 RACSubscriber
协议的吧,名为 didSubscribe
的block,具体实现以下:设计
由上图可知,内部建立的是一个 RACDynamicSignal
类型的信号,并将 didSubscribe
传入,内部实现以下:3d
这里就是重点了,首先先建立了一个 RACDynamicSignal
类型的信号,而后将传入的名为 didSubscribe
的block保存在建立的信号的 didSubscribe
属性中,此时仅仅是保存并未触发。code
一句话总结:建立信号本质就是建立了一个 RACDynamicSignal
类型的信号,并将传入的代码块保存起来,留待之后调用。cdn
由上面的 信号类使用图
可知,有三种订阅信号的方式,分别是订阅 Next
、Error
、 completed
,内部实现以下:对象
首先咱们来看第一步,就是建立一个订阅者,并传入相应的block,建立实现以下:blog
很明显,建立订阅者的实质是,建立一个订阅者,并保存相应的block,好比 net
、error
、或者complete
,此时仅仅是保存并未触发!
由上面两图可知,三种订阅方式的流程模式是一致的,仅仅是保存的block不一样而已,咱们分析一种便可,so 接下来就以 subscribeNext
为例来逐步分析。
接下来,咱们看看执行订阅命令这块的实现,以下:
这里,首先咱们要知道此处代码实现是在 RACDynamicSignal
里,图中的 didSubscribe
就是第一步建立信号中保存的 didSubscribe
block。
由上图可知第一步建立信号中保存的 didSubscribe
代码块在这里执行,并传入了刚刚生成的订阅者(此处的订阅者中保存里 Next
block代码块)。
额外的,这里生成了一个 RACCompoundDisposable
类型的disposable,用来管理整个订阅结束及资源的清理,并以传入的订阅者、及当前信号、刚建立的disposable 生成一个 RACPassthroughSubscriber
类型的订阅者,此订阅者仅仅是将传入的三个对象总体包装了一下而已,实质起做用的仍是在刚才建立的订阅者,因此,其包含的 next
代码块,依然直接调用便可。
以后,将执行 didSubscribe
代码块返回的 innerDisposable 传入刚刚生成disposable、并将执行此代码块的信号的 schedulingDisposable 也保存到RACCompoundDisposable
类型的disposable中,而后统一管理整个订阅结束及资源的清理。
其中的innerDisposable 就是 上面的 信号类使用图
中表示的第四步,其会在信号结束订阅的时候被调用,作一些清理资源的工做。
而 schedulingDisposable 的话,实际上就是执行代码块放到相应的调度器中,假如没有设置的话,即为 backgroundScheduler
,并异步执行 didSubscribe
代码块,假如设置的话就会返回nil,并直接执行代码块,此处嵌套就有点深了,随后讲解取消订阅的时候再细说。
此处用到的RACCompoundDisposable
类型的disposable有点相似于可变数组 NSMutableArray 。而当 RACCompoundDisposable
对象被 disposed 时,它会调用其所包含的全部 disposable 对象的 -dispose 方法。因此能够作统一管理。
一句话总结:订阅信号本质就是建立了一个 RACPassthroughSubscriber
类型的订阅者,并将传入的代码块保存起来,留待之后调用,同时调用了第一步建立信号中保存的代码块,并传入建立的订阅者。
由上面的 信号类使用图
可知,发送一样对应着三种方式,处理以下:
这里有三点须要知道
第一,发送信号就是执行相应block,此处执行的就是第二步中保存的相应的block。
第二,对于 sendError
和 sendCompleted
都是先取消订阅,再执行相应的代码块,而 sendNext
并未使订阅结束,这样的话,对以后讨论的各类组合方法中必须写上 sendCompleted
来结束订阅的作法就好理解了。
第三,咱们也看到三种方法中,假如信号没有相应的block代码块保存,即没有通过第二步去订阅保存代码块,就算是发送了信号也不会执行,此时也就是冷热信号的区别,固然用 RACSubject
来解释更容易理解。
一句话总结:发送信号就是执行订阅信号时对应的block。
通过上面三步,咱们也了解到了想要结束订阅只要将相应生成的disposable执行dispose
便可,那到底为何呢?如今来说一讲:
第二步订阅信号中有讲到,执行订阅的代码块其实是放到相应的调度器中去执行的,以下图:
如上图,是否是颇有疑问,假如currentScheduler
不为nil的话,那岂不是没有dispose
啥事,代码块就直接执行了?我当时也纳闷,不过框架中叙述以下(此处感谢雷神指导,么么哒!):
此时:代码块就不会放到调度器里去执行,而是直接执行,此时disposable的dispose
方法就没啥卵用了。
假如currentScheduler
为nil的话,会默认一个调度器去管理代码块,具体实现以下:
如图所示,可知在代码块未被调度的以前,生成的disposable被dispose
的话,代码块就不会被执行。
一句话总结:取消订阅就是把订阅信号得到的disposable 进行dispose便可在调度器调度该部分代码以前禁止调用。
RACSignal的原理搞清了,接下来就比较好理解了,毕竟信号类都是继承与RACSignal,做用不一样也仅仅是部分实现的方式不一样而已。
以下图:
由上图可知,建立RACSubject对象的时候同时建立了相应的一个disposable和一个订阅者数组,没有作其余事情。
一句话总结:建立信号就是额外实例化了一个订阅者数组,没有作其余事情。
大体流程与RACSignal是一致的,不一样点在下面:
RACSinal订阅的时候直接执行了订阅命令,执行的是建立时保存的代码块,但RACSubject的作法是将订阅者保存到初始化时生成的那个订阅者数组内,此时每一个订阅者都包含其对应的代码块(好比:next、error、complete)。
由此能够知道,RACSubject是能够屡次订阅的,屡次订阅就是把相应包含代码块的订阅者放入订阅者数组内。
另外,此处对于取消订阅作了一些清理工做,将保存的订阅者都移除。
一句话总结:订阅信号就是将代码块保存到订阅者中,并将订阅者添加到信号的订阅者数组中。
大体流程与RACSignal是一致的,不一样点在下面:
由上图可知,RACSubject的发送信号就是遍历本身的订阅者数组,而后分别发送信号。
这也是对月RACsubjec为何假若有多个订阅者,发送一个信号全部的订阅者都收到的缘由。
一句话总结:发送信号就是遍历本身的订阅者数组,而后分别发送信号,并执行该订阅者保存的代码块。
仍是那句话,信号类都是继承与RACSignal,做用不一样也仅仅是部分实现的方式不一样而已。
如不一样的订阅信号实现:
不一样的发送信号实现:
授人以鱼不如授人以渔,知道了这种分析流程方法,其余相应的分析相似,朋友们能够本身试试分析一下,这样你就会发现其实并非那么难!
这个就比较复杂了,由于他是基于信号的一个对象,里面绑定了不少相关的信号,这里只是讲一讲他的执行原理,及经常使用的一些方法的分析。
先上图以下:
由上面 RACCommand流程图
可知建立的时候传入一个返回值为RACSignal类型,参数为id类型的名为input的一个block,具体实现以下:
由上图可知,command的建立实质是额外建立了一个名为 _activeExecutionSignals
的信号数组,并把传入的block保存为 signalBlock
属性。
初始化的时候实际上还有不少其余绑定的代码,就不一一细说了,用到的下面会讲一下。
一句话总结:建立命令就是建立了一个RACCommand类的对象,并将传入的block保存为 signalBlock
属性,而后初始化了一个信号数组,留待之后接收命令信号。
由上面 RACCommand流程图
可知订阅的信号通过了两步方法,第一步中executionSignals为建立命令时绑定的信号以下:
由上图可知,executionSignals实际上就是最新的第一步建立命令中建立的activeExecutionSignals信号数组。
第二步中的switchToLatest实质上取信号数组的最新的信号。
而后就订阅信号,月RACSignal的订阅流程同样,再也不赘述。
一句话总结:订阅命令实质上就是订阅命令保存的信号数组中的最新的信号(目前代码的订阅)
与executionSignals相似,在建立命令时也建立了一个信号来监听当前命令是否正在执行。
如上图所示,executing信号其实是在检测是否有活跃信号,replayLast能确保是最新的值。
一句话总结:判断是否执行命令就是检测是否有活跃信号
执行命令的话就是传入你所须要传入的对象便可,以下图
由上图,咱们能够知道,执行命令先执行了第一步保存的signalBlock并传入接收到的参数,而后把获得的signal加入到第一步建立的 _activeExecutionSignals
信号数组中,留待第二步订阅使用。
一句话总结:执行命令就是将传入的对象传入signalBlock生成signal,并将signal添加到_activeExecutionSignals
信号数组中。
在实际开发中咱们并不须要直接调用bind方法,但咱们所使用API都是以bind方法为基础的,想要了解操做方法的实现原理,bind咱们不得不深究一下。
先上 bind
流程图以下:
由上图可知,bind
操做方法的流程以下:
由此可知,与正常信号绑定订阅流程不一样之处在于,多了一个绑定过程,而且最终订阅的是绑定生成的绑定信号。接下来咱们一个一个理一下思路:
一句话总结:建立了一个 RACDynamicSignal
类型的信号,并将传入的代码块保存起来,留待之后调用。
由 bind操做流程图
可知,bind
方法传入了一个 RACStream * (^RACStreamBindBlock)(id value, BOOL *stop)
的block代码块,其具体实现以下(只截取核心的代码):
由上图可知,bind操做实际上直接返回了一个绑定信号信号,并将 didSubscriber
代码块 传入,保存到返回的信号内。
额外的,绑定信号的 didSubscriber
代码块 中作了两件事
第一,将bind方法传入的block保存为bindinfBlock,留待之后用,而且初始化了一个信号数组。
第二,在代码块内部订阅了源信号,并作了一些处理,以下:
由上面可知,订阅源信号传入的block,被保存在相应订阅者中,留待源信号发送信号触发,内部实现再也不赘述。
一句话总结:bind操做其实是直接生成绑定信号并返回,而且在生成绑定信号传入的didSubscriber
block代码块中,保存了bind传入的block,初始化了信号数组,而且订阅了源信号,针对源信号发送信号的流程作了一些处理。(此时未执行,订阅才执行)
订阅绑定信号就是实现了第二步中的 didSubscriber
代码块,就是说,第二步仅仅是保存,在订阅它的时候才会实现。
一句话总结:订阅绑定信号就是保存了nextBlock
,而且建立订阅者,实现信号的didSubscriber
block代码块。
由于bind比较复杂,因此在此能够将其串起来
一、发送信号触发绑定信号 didSubscriber
代码块中订阅源信号时传入的NextBlock
代码块,以下:
二、是执行第三步中订阅绑定信号保存的从bind传入的额block代码块,并传入由订阅时的传参生成的新的传参,生成returnSignal,具体实现以下:
三、returnSignal内部实现以下
由代码可知,returnSignal其实是保存了传入的新值,此时就是真正意义上的将源输入作了改变,最后实际输出的就是新保存的值。
四、接下来就是将生成的returnSignal作 addSignal
操做以下:
由上图代码可知,内部实现是直接订阅了传入的returnSignal。
五、以前咱们也说过,不一样信号对应不一样的订阅代码,因此咱们来看看returnSignal的订阅时的代码,以下:
由上图代码可知,returnSignal在订阅的时候就同时作了发送信号的任务,即直接就会走订阅保存的代码块,以下:
此处触发的是绑定信号的订阅者发送信号,就是执行绑定信号的 nextBlock
,即:
此时就有了运行结果:
ok,bind操做方法就到这了,我已经用尽了个人洪荒之力,么么哒!
一句话总结:bind操做方法实质上就是生成新的绑定信号,利用returnSignal做为中间信号来改变源数据生成新的数据并执行新绑定信号的nextBlock代码块!
全部操做方法都是以bind方法为核心的,这里分析一下映射方法便可,不在赘述其余方法,原理都是同样的,就是看怎么组合,怎么去处理相关逻辑达到不一样操做方法达成的效果!
首先,看一下map的操做流程图,以下:
很显然,与 bind
的区别就是生成绑定信号的过程不一样,具体实现以下:
由上图可知,从外面传入了一个参数为value,返回值为id类型的block代码块,时机返回的是 flattenMap
,操做方法生成的信号,具体实现以下:
看,就是这里,在 map
方法中,返回的是 flattenMap
方法生成的信号,而 flattenMap
内部返回的是 bind
方法返回的信号,又回到了第一部分讲解的 bind
实现原理
上图中的 stream
,就是returnSignal,即 flattenMap
方法传入的 block(value),以下图:
具体生成ReturnSignal的方法,还得往回看 map
方法以下:
由上图可知,传入的value已经被 map
保存的方法所映射(即改变),以下:
一句话总结:map
映射方法的实质就是 bind
方法的深度封装,实际的信号流的流程仍是以 bind
为核心,只是巧妙的作了一些逻辑处理而已。
对于操做方法,核心就是 bind
,各类做用的不一样就是各类 signal
与 bind
方法的结合,外加一些巧妙的逻辑处理,明白了信号的原理、绑定的原理,任何其余的组合或者运用都是万变不离其宗的,写更多也不如本身去研究一下!么么哒!
因此,操做方法就写到这里!
额,这样就差很少了吧,弄明白了本篇所介绍的几个经常使用的最基本的原理,其实已经对RAC了解的七七八八了,固然RAC还有不少值得去学习的思想,好比RAC宏的使用,各类UI操做的处理,他们其中的设计与原理也都很巧妙,网上也有不少博客介绍了,这里就暂时不深刻探讨了,毕竟写到这里篇幅就已经够长了,你们有兴趣能够本身去看看,之后有时间了我在整理一下!
本文由做者 王隆帅 编写,转载请保留版权网址,感谢您的理解与分享,让生活变的更美好!