iOS开发攻城狮的集散地iOS 进阶开发ReactiveCocoa

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

2019-06-02  本文已影响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函数用到了极致!

上一篇下一篇

猜你喜欢

热点阅读