RACMulticastConnection

文章系列
《ReactiveCocoa 概述》
《RACSignal》
《RACDisposable》
《RACSubject漱竖、RACReplaySubject(內(nèi)附冷信號和熱信號的區(qū)別)》
《集合RACTuple禽篱、RACSequence》
《RAC 中的通知、代理馍惹、KVO, 基本事件躺率、方法的監(jiān)聽》
《rac_liftSelector》
《RACMulticastConnection》
《RACCommand》
《RAC - 核心方法bind》
《RAC - 定時器》
《RACScheduler》
《RAC - 點(diǎn)擊獲取驗(yàn)證碼 demo》
《RAC - 映射(Map & flattenMap)》
《RAC信號操作解釋合集》
《RAC - 信號的生命周期》

  • 發(fā)現(xiàn)問題: 一般情況下,信號被訂閱多少次,信號創(chuàng)建時的block 就調(diào)用多少次

    // 以網(wǎng)絡(luò)請求的信號被多次訂閱為例:
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
       
        NSLog(@"請求數(shù)據(jù)");
        [subscriber sendNext:@"數(shù)據(jù)是回來啦"];
        return  nil;
    }];
    
    [signalA subscribeNext:^(id  _Nullable x) {
        NSLog(@"第一次訂閱=%@", x);
    }];
    
    [signalA subscribeNext:^(id  _Nullable x) {
        NSLog(@"第二次訂閱=%@", x);
    }];
    
    [signalA subscribeNext:^(id  _Nullable x) {
        NSLog(@"第三次訂閱=%@", x);
    }];

打印結(jié)果:
進(jìn)行了三次網(wǎng)絡(luò)請求
  • 解決原因及方法: 如果創(chuàng)建信號的block 中代碼性能開銷很大并且重復(fù)執(zhí)行結(jié)果相同,那么確保block 中的代碼只被執(zhí)行一次就很有意義,Multicast就是做的這個事情.

    // RACMulticastConnection 其實(shí)是一個連接類,可以實(shí)現(xiàn)不管訂閱多少次信號,信號的block 都只請求一次
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
       
        NSLog(@"請求數(shù)據(jù)");
        [subscriber sendNext:@"數(shù)據(jù)是回來啦"];
        return  nil;
    }];

    // 將信號轉(zhuǎn)成連接類
    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];
注:結(jié)尾處必須使用[connection connect]進(jìn)行連接才有效果.

打印結(jié)果:
RACMulticastConnection 連接類訂閱信號, block 只調(diào)用一次
  • RACMulticastConnection 內(nèi)部實(shí)現(xiàn)原理分析

通過 RACMulticastConnection *connection = [signalA publish]; 點(diǎn)擊查看publish方法 的實(shí)現(xiàn), 如下(省略非關(guān)鍵代碼):

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

multicast: 的內(nèi)部實(shí)現(xiàn)↓

- (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自身和 傳遞進(jìn)來的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 其實(shí)本質(zhì)是初始化方法時傳遞進(jìn)來的subject
    _signal = subject;
    
    return self;
}
  1. 私有的源信號sourceSignal 即: 沒有進(jìn)行public 操作的signal
  2. 本質(zhì)為RACSubject_signal對象, 即:public操作時創(chuàng)建的對象(對應(yīng)注釋1)

↓所以在連接類進(jìn)行信號訂閱的時候[connection.signal subscribeNext:^...], 本質(zhì)是這樣的↓

SubscribeNext-To-RACSubject-Before-Connect

  1. 訂閱 connection.signal 中的數(shù)據(jù)流時,其實(shí)只是向多播對象中的熱信號 RACSubject 持有的數(shù)組中加入訂閱者万矾,而這時剛剛創(chuàng)建的 RACSubject 中并沒有任何的消息悼吱。

↓只有在調(diào)用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 代碼塊才會執(zhí)行, 向 RACSubject 推送消息良狈,消息向下繼續(xù)傳遞到 RACSubject 所有的訂閱者中后添。
    Values-From-RACSignal-To-Subscribers
  2. -connect 方法通過 -subscribe: 實(shí)際上建立了 RACSignal 和 RACSubject 之間的連接,這種方式保證了 RACSignal 中的 didSubscribe 代碼塊只執(zhí)行了一次薪丁。
  3. 所有的訂閱者不再訂閱原信號遇西,而是訂閱 RACMulticastConnection 持有的熱信號 RACSubject,實(shí)現(xiàn)對冷信號的一對多傳播
  • 使用 RACReplaySubject 訂閱源信號

雖然使用 -publish 方法已經(jīng)能夠解決大部分問題了窥突,但是在 -connect 方法調(diào)用之后才訂閱的訂閱者并不能收到消息努溃。

如何才能保存 didSubscribe 執(zhí)行過程中發(fā)送的消息,并在 -connect 調(diào)用之后也可以收到消息阻问?這時梧税,我們就要使用 -multicast: 方法和 RACReplaySubject 來完成這個需求了。

↓具體操作就是講publish替換成replay

RACMulticastConnection *connection = [signalA replay];

內(nèi)部實(shí)現(xiàn)的思想也都類似.

除了 -replay 方法称近,RACSignal 中還定義了與 RACMulticastConnection 中相關(guān)的其它 -replay 方法:

    - (RACSignal<ValueType> *)replay;
    // 方法生成的 RACMulticastConnection 中熱信號的容量為 1:
    - (RACSignal<ValueType> *)replayLast;
    // replayLazily 會在返回的信號被第一次訂閱時第队,才會執(zhí)行 -connect 方法:
    - (RACSignal<ValueType> *)replayLazily;

.End

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刨秆,隨后出現(xiàn)的幾起案子凳谦,更是在濱河造成了極大的恐慌,老刑警劉巖衡未,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尸执,死亡現(xiàn)場離奇詭異家凯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)如失,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門绊诲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人褪贵,你說我怎么就攤上這事掂之。” “怎么了脆丁?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵世舰,是天一觀的道長。 經(jīng)常有香客問我槽卫,道長跟压,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任晒夹,我火速辦了婚禮裆馒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丐怯。我一直安慰自己喷好,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布读跷。 她就那樣靜靜地躺著梗搅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪效览。 梳的紋絲不亂的頭發(fā)上无切,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音丐枉,去河邊找鬼哆键。 笑死,一個胖子當(dāng)著我的面吹牛瘦锹,可吹牛的內(nèi)容都是我干的籍嘹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼弯院,長吁一口氣:“原來是場噩夢啊……” “哼辱士!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起听绳,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤颂碘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后椅挣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體头岔,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡塔拳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了切油。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝙斜。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖澎胡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情娩鹉,我是刑警寧澤攻谁,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站弯予,受9級特大地震影響戚宦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锈嫩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一受楼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呼寸,春花似錦艳汽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瑟捣,卻和暖如春馋艺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迈套。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工捐祠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桑李。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓踱蛀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芙扎。 傳聞我的和親對象是個殘疾皇子星岗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359