美文网首页
RACMulticastConnection

RACMulticastConnection

作者: 下班不写程序 | 来源:发表于2020-10-14 10:06 被阅读0次

    文章系列
    《ReactiveCocoa 概述》
    《RACSignal》
    《RACDisposable》
    《RACSubject、RACReplaySubject(内附冷信号和热信号的区别)》
    《集合RACTuple、RACSequence》
    《RAC 中的通知、代理、KVO, 基本事件、方法的监听》
    《rac_liftSelector》
    《RACMulticastConnection》
    《RACCommand》
    《RAC - 核心方法bind》
    《RAC - 定时器》
    《RACScheduler》
    《RAC - 点击获取验证码 demo》
    《RAC - 映射(Map & flattenMap)》
    《RAC信号操作解释合集》
    《RAC - 信号的生命周期》

    • 发现问题: 一般情况下,信号被订阅多少次,信号创建时的block 就调用多少次

        // 以网络请求的信号被多次订阅为例:
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
           
            NSLog(@"请求数据");
            [subscriber sendNext:@"数据是回来啦"];
            return  nil;
        }];
        
        [signalA subscribeNext:^(id  _Nullable x) {
            NSLog(@"第一次订阅=%@", x);
        }];
        
        [signalA subscribeNext:^(id  _Nullable x) {
            NSLog(@"第二次订阅=%@", x);
        }];
        
        [signalA subscribeNext:^(id  _Nullable x) {
            NSLog(@"第三次订阅=%@", x);
        }];
    
    打印结果: 进行了三次网络请求
    • 解决原因及方法: 如果创建信号的block 中代码性能开销很大并且重复执行结果相同,那么确保block 中的代码只被执行一次就很有意义,Multicast就是做的这个事情.

        // RACMulticastConnection 其实是一个连接类,可以实现不管订阅多少次信号,信号的block 都只请求一次
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
           
            NSLog(@"请求数据");
            [subscriber sendNext:@"数据是回来啦"];
            return  nil;
        }];
    
        // 将信号转成连接类
        RACMulticastConnection *connection = [signalA publish];
        // 订阅连接类信号
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"第一次连接类信号订阅=%@", x);
        }];
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"第二次连接类信号订阅=%@", x);
        }];
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"第三次连接类信号订阅=%@", x);
        }];
        // 连接
        [connection connect];
    
    注:结尾处必须使用[connection connect]进行连接才有效果.
    打印结果: RACMulticastConnection 连接类订阅信号, block 只调用一次
    • RACMulticastConnection 内部实现原理分析

    通过 RACMulticastConnection *connection = [signalA publish]; 点击查看publish方法 的实现, 如下(省略非关键代码):

    - (RACMulticastConnection *)publish {
        RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
        RACMulticastConnection *connection = [self multicast:subject];
        return connection;
    }
    
    1. 在publish 方法中首先创建了一个subject.
    2. 通过[self multicast:subject]保存在connection 中

    multicast: 的内部实现↓

    - (RACMulticastConnection *)multicast:(RACSubject *)subject {
        [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
        RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
        return connection;
    }
    
    1. RACMulticastConnection的初始化方法alloc init操作保存signal自身和 传递进来的subject.

    RACMulticastConnection 是如何保存signal subject???

    RACMulticastConnection类属性
    - (instancetype)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {
        NSCParameterAssert(source != nil);
        NSCParameterAssert(subject != nil);
    
        self = [super init];
    
        // 定义'只读属性'保存原始信号signal 
        _sourceSignal = source;
        _serialDisposable = [[RACSerialDisposable alloc] init];
       // 对外暴露属性signal 其实本质是初始化方法时传递进来的subject
        _signal = subject;
        
        return self;
    }
    
    1. 私有的源信号sourceSignal 即: 没有进行public 操作的signal
    2. 本质为RACSubject_signal对象, 即:public操作时创建的对象(对应注释1)

    ↓所以在连接类进行信号订阅的时候[connection.signal subscribeNext:^...], 本质是这样的↓

    SubscribeNext-To-RACSubject-Before-Connect
    1. 订阅 connection.signal 中的数据流时,其实只是向多播对象中的热信号 RACSubject 持有的数组中加入订阅者,而这时刚刚创建的 RACSubject 中并没有任何的消息。

    ↓只有在调用connect方法之后,RACSubject才会订阅源信号 sourceSignal

    - (RACDisposable *)connect {
        BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);
    
        if (shouldConnect) {
            self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
        }
    
        return self.serialDisposable;
    }
    
    1. 这时, 源信号的 didSubscribe 代码块才会执行, 向 RACSubject 推送消息,消息向下继续传递到 RACSubject 所有的订阅者中。 Values-From-RACSignal-To-Subscribers
    2. -connect 方法通过 -subscribe: 实际上建立了 RACSignal 和 RACSubject 之间的连接,这种方式保证了 RACSignal 中的 didSubscribe 代码块只执行了一次。
    3. 所有的订阅者不再订阅原信号,而是订阅 RACMulticastConnection 持有的热信号 RACSubject,实现对冷信号的一对多传播
    • 使用 RACReplaySubject 订阅源信号

    虽然使用 -publish 方法已经能够解决大部分问题了,但是在 -connect 方法调用之后才订阅的订阅者并不能收到消息。

    如何才能保存 didSubscribe 执行过程中发送的消息,并在 -connect 调用之后也可以收到消息?这时,我们就要使用 -multicast: 方法和 RACReplaySubject 来完成这个需求了。

    ↓具体操作就是讲publish替换成replay

    RACMulticastConnection *connection = [signalA replay];
    

    内部实现的思想也都类似.

    除了 -replay 方法,RACSignal 中还定义了与 RACMulticastConnection 中相关的其它 -replay 方法:

        - (RACSignal<ValueType> *)replay;
        // 方法生成的 RACMulticastConnection 中热信号的容量为 1:
        - (RACSignal<ValueType> *)replayLast;
        // replayLazily 会在返回的信号被第一次订阅时,才会执行 -connect 方法:
        - (RACSignal<ValueType> *)replayLazily;
    

    .End

    相关文章

      网友评论

          本文标题:RACMulticastConnection

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