RACMulticastConnection

文章系列
《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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352