美文网首页iOS开发攻城狮的集散地iOS 进阶开发ReactiveCocoa
探究ReactiveCocoa底层之深入了解RACSignal生

探究ReactiveCocoa底层之深入了解RACSignal生

作者: 溪浣双鲤 | 来源:发表于2019-06-02 23:53 被阅读52次

               前言:自己做iOS移动开发也这么久了,相比较Swift的语法糖和新特性,OC显得异常的笨重,不管是写新的业务逻辑还是维护老项目,OC都太过于拖泥带水了,胶水代码一大堆,典型的例子就是UI层面的target-action,当项目C层代码业务复杂并且没有做解耦操作的时候,找一个按钮的点击响应事件,都要经历好几步。代码分散以及过多的胶水代码会让我们的代码业务逻辑不清晰,并且不容易维护。而上面这些问题在ReactiveCocoa函数响应式编程思想下就显得异常简单,逻辑严谨,不多废话了,让我们来深入分析一下信号的生命流程,我已经迫不及待的要开始安利给你们用了O(∩_∩)O哈!

    一、深入分析信号RACSignal的生命周期

    图1、信号的生命流程

    下面是一个最常用的信号操作,让我们结合代码来一层层分析它的实现

    // 1: 创建信号/初始化信号

        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

            // 3: 发送信号

            [subscriber  sendNext:@"Cooci"];

           // 4、: 取消订阅

            [subscriber   sendCompleted];

            return [RACDisposable disposableWithBlock:^{

                NSLog(@"释放了");

            }];

        }];

          // 2: 订阅信号

        [signal   subscribeNext:^(id  _Nullablex) {

            NSLog(@"订阅:%@",x);

        }];

    1、创建信号

    首先调用RACSignal的类方法,我们可以找到RACSignal的createSignal方法并能够看到createSignal是封装了RACDynamicSignal类的createSignal方法

    + (RACSignal*)createSignal:(RACDisposable* (^)(id subscriber))didSubscribe {

           return[RACDynamicSignal  createSignal:didSubscribe];

    }

    点击进去能看到这个类初始化一个RACDynamicSignal实例对象,这个信号量实例对象使用了函数式的编程思想把整个didSubscribe代码块都保存了下来,这里就充分体现了block代码块的优点,这块代码会一直被这个dynamicSignal所持有

    + (RACSignal*)createSignal:(RACDisposable* (^)(id subscriber))didSubscribe {

       RACDynamicSignal*signal = [[self alloc]init];

        // 函数式 --- 保存block 灵活 -- 

       //初始化一个RACDynamicSignal对像并返回,同时把在创建的时候传入一个didSubscribe代码块也保存在信号里面,代码块留着以后调用,灵活度很高,此处代码块记为代码块1,方便流程分析

       signal->_didSubscribe= [didSubscribe  copy];

       return [signal  setNameWithFormat:@"+createSignal:"];

    }

    2、订阅信号

    创建完成并保存了一个didSubscribe代码块之后,要想使用这个信号,就必须订阅信号,我们来看一下订阅信号里面做了什么

    - (RACDisposable*)subscribeNext:(void(^)(idx))nextBlock {

       NSCParameterAssert(nextBlock != NULL);

     //根据传入的nextBlock,初始化一个RACSubscriber对象,目的是让RACSubscriber对象持有这个代码块,这个代码块也是留着以后调用,灵活度很高。此代码块记为代码块2

       RACSubscriber *o = [RACSubscriber    subscriberWithNext:nextBlock    error:NULL completed:NULL];

        return [self subscribe:o];

    }

    可以看到订阅信号,也会保存一个nextBlock代码块,并且整个操作会返回一个RACDisposable对象,后面会细说返回值RACDisposable作用,从上面这块代码我们能看出在订阅的时候传入并初始化了一个RACSubscriber对象来持有并保存nextBlock代码块,看到这里,我们不免有疑问,为什么要用block初始化一个RACSubscriber,RACSubscriber是用来干嘛的?为什么返回一个 RACDisposable对象,这个对象是用来干嘛的?请看下图:

    - (RACDisposable*)subscribe:(id)subscriber {

    NSCParameterAssert(subscriber !=nil);

        // dispose

     //1、这里又初始化了一个RACCompoundDisposable对象,用来管理整个订阅结束及资源的清理

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];

    //2、这里又初始化了一个 RACPassthroughSubscriber对象,并用这个新对象同时保存了传入的subscriber(其中有订阅传入的代码块),disposable(上面一步新建的RACCompoundDisposable对象),还有self(也就是当前这个信号量RACDynamicSignal对象),此这个RACPassthroughSubscriber订阅者仅仅是将传入的三个对象整体包装了一下而已,实质起作用的还是在刚才创建的订阅者。需要注意的是,到目前为止,这个订阅传入的nextBlock和之前初始化的那个didSubscribeBlock,这两个block都只是保存了代码块暂时并没有触发!

    subscriber = [[RACPassthroughSubscriber alloc]initWithSubscriber:subscriber signal:self disposable:disposable];

    if (self.didSubscribe != NULL) {

       //3、加入调度等等等的处理,重点来了,这里调用初始化传入的那个代码块。此处调用代码块1

    RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler    schedule:^{

         RACDisposable*innerDisposable =self.didSubscribe(subscriber);//⚠️此处调用当前信号,初始化时候保存的那个block,那个block的调用就在这里!!!

    此处的innerDisposable主要负责取消订阅后对象的清除操作,所以会有添加操作,有add肯定就有remove,对应订阅对象的添加和清除操作

         [disposable    addDisposable:innerDisposable];

    }];

    [disposable  addDisposable:schedulingDisposable];

    }

    return    disposable;

    }

    图2、信号生命周期分析-订阅信号

    分析以上流程不难发现,其实订阅信号的本质就是创建了一个 RACPassthroughSubscriber 类型的订阅者,并将传入的nextBlock代码块(代码块2)保存起来,留待以后调用,同时调用了第一步创建信号中保存的代码块1,并传入创建的订阅者。

    3、发送信号和取消订阅

    发送信号,本质上其实就是执行相应的代码块block,而在这里我们目的就是要执行订阅信号传入的那个代码块2,这个调用写在了代码块1里面,我们再回头看一下代码块1的实现部分就很清楚明了了

    图3、发送信号,发送完后取消订阅

    二、流程及设计思想总结:

    1、信号量生命流程图:

    图3、信号量的生命流程

    2、设计思想总结:

           把中间的包装层撇开不谈,RACSignal信号量底层实现无非就是在创建和订阅的同时分别保存了两个block并且在订阅的时候实现初始化保存的那个block(代码块1),然后代码块1中调用订阅信号时保存的block(代码块2),充分利用函数式和响应式思想,实现两个代码块的互相调用闭环,并且将block函数用到了极致!

    相关文章

      网友评论

        本文标题:探究ReactiveCocoa底层之深入了解RACSignal生

        本文链接:https://www.haomeiwen.com/subject/izlxxctx.html