ReactiveCocoa 學(xué)習(xí)筆記(二)之 RACMulticastConnection

A multicast connection encapsulates the idea of sharing one subscription to a signal to many subscribers. This is most often needed if the subscription to the underlying signal involves side-effects or shouldn't be called more than once.

多播連接 封裝了多個訂閱者共享同一份訂閱的這樣一個概念沟涨,通常來說营罢,當(dāng)對底層的 signal 的訂閱會產(chǎn)生很多副作用或者這個訂閱不應(yīng)該被多次調(diào)用時才會需要它。

源碼的注釋只給了我們一個很模糊的概念鹤啡,下面我們通過閱讀源碼來看看是如何實(shí)現(xiàn)的并且它究竟在實(shí)戰(zhàn)中有什么用處。

首先我們看頭文件:

@interface RACMulticastConnection : NSObject

@property (nonatomic, strong, readonly) RACSignal *signal;
- (RACDisposable *)connect;
...

@end

signal 屬性就是用于多播的信號粤策,connect 用于和底層的信號建立聯(lián)系岛请,先不管 autoconnect秕硝。

接著看實(shí)現(xiàn)文件:

@interface RACMulticastConnection () {
 RACSubject *_signal;
 int32_t volatile _hasConnected;
}

@property (nonatomic, readonly, strong) RACSignal *sourceSignal;
@property (strong) RACSerialDisposable *serialDisposable;
@end

@implementation RACMulticastConnection

#pragma mark Lifecycle

- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {
 NSCParameterAssert(source != nil);
 NSCParameterAssert(subject != nil);

 self = [super init];
 if (self == nil) return nil;

 _sourceSignal = source;
 _serialDisposable = [[RACSerialDisposable alloc] init];
 _signal = subject;
 
 return self;
}

#pragma mark Connecting

- (RACDisposable *)connect {
 BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);

 if (shouldConnect) {
  self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
 }

 return self.serialDisposable;
}
...
}

_signal 是一個私有的 ivar,類型是 RACSubject捷枯,它是 RACSignal 的子類滚秩,所以 signal property 背后的 ivar 就是這個 _signal。

_hasConnected 是一個標(biāo)志量淮捆,用于標(biāo)識是否已經(jīng)建立連接郁油,無論調(diào)用多少次 connect 都只會建立一次連接本股。

sourceSignal 是源信號,這個信號是外部傳入的那個我們自己創(chuàng)建的信號桐腌。

initWithSourceSignal:subject: 是初始化方法拄显,第一個參數(shù)是用于多播的源信號,第二個參數(shù)是用于建立連接的信號案站,方法內(nèi)部實(shí)現(xiàn)很簡單躬审,就是對屬性進(jìn)行賦值。

connect 方法首先判斷是否已經(jīng)建立連接蟆盐,如果沒有的話承边,就調(diào)用 sourceSignal 的 subscribe 方法,將 _signal 注冊到源信號石挂。

在 RAC 中用到 RACMulticastConnection 的地方是在 RACSignal 的 operation 分類中的這些方法中:

  • publish
  • multicast:
  • replay
  • replayLast
  • replayLazily

我們以 replay 方法舉例:

- (RACSignal *)replay {
 RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"[%@] -replay", self.name];

 RACMulticastConnection *connection = [self multicast:subject];
 [connection connect];

 return connection.signal;
}

- (RACMulticastConnection *)multicast:(RACSubject *)subject {
 [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
 RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
 return connection;
}

首先創(chuàng)建一個 RACReplaySubject博助,這個 subject 可以將值流中的值保存下來,每次訂閱都會將所有保存的值發(fā)出去痹愚,然后通過 multicast: 方法傳入剛才創(chuàng)建的 subject 新建一個 RACMulticastConnection富岳,multicast: 方法也很簡單,直接調(diào)用的是 RACMulticastConnection 的初始化方法拯腮,分別傳入 self 和 subject窖式,最后調(diào)用 connect 方法。

通過(一)动壤,創(chuàng)建一個 signal 實(shí)際上內(nèi)部是創(chuàng)建的 RACDynamicSignal萝喘,并把 didSubscribe block 保存在 signal 內(nèi)部。當(dāng) signal 調(diào)用以 subscribe 開頭的方法時狼电,會新建一個 RACSubscriber 然后調(diào)用 subscribe 方法。

在這里弦蹂,我們沒有調(diào)用 signal 的以 subscribe 開頭的方法肩碟,而是在 connect 方法內(nèi)部直接調(diào)用 signal 的 subscribe 方法,該方法會調(diào)用 didSubscribe 方法凸椿,傳入的 subject 就直接作為訂閱者來接收值削祈,并且這些值被保存了下來。

接下來回到 replay 方法脑漫,它返回的是 connection.signal髓抑,前面已經(jīng)提到這個 signal 就是之前創(chuàng)建的 subject,之后每一次對這個 signal 進(jìn)行訂閱优幸,都不會再去調(diào)用源信號的 didSubscirbe 方法吨拍,而是去調(diào)用 subject 重寫的 subscribe: 方法,這個方法會調(diào)用 subject 所有的 subscriber 的 sendNext: 方法网杆,把保存的值都傳出去羹饰,最后能夠經(jīng)過一系列的轉(zhuǎn)換后調(diào)用到 nextBlock伊滋,從而結(jié)束整個流程。

到這里基本分析完了 RACMulticastConnection 是怎么做的队秩,接下來看看它能做什么笑旺。

replay 會讓源信號只被訂閱一次,并把所有的值保存下來馍资,也即是 didSubscribe 只被調(diào)用一次筒主,之后的每次訂閱都會把保存的所有值發(fā)送出去。

replayLast 和 replay 很類似鸟蟹,其實(shí)只是做了一個限制乌妙,保存最近的一個值。

replayLazily 需要好好分析一下戏锹,看下面的源碼:

- (RACSignal *)replayLazily {
 RACMulticastConnection *connection = [self multicast:[RACReplaySubject subject]];
 return [[RACSignal
  defer:^{
   [connection connect];
   return connection.signal;
  }]
  setNameWithFormat:@"[%@] -replayLazily", self.name];
}

依舊是把 self 作為源信號冠胯,新建了一個 RACReplaySubject 作為用于多播的 signal,不同的是锦针,這里用 defer 新建了一個 signal荠察,并且在 defer block 里面才建立連接。

defer 有什么用呢奈搜?繼續(xù)看源碼:

+ (RACSignal *)defer:(RACSignal * (^)(void))block {
 NSCParameterAssert(block != NULL);

 return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
  return [block() subscribe:subscriber];
 }] setNameWithFormat:@"+defer:"];
}

defer 會創(chuàng)建一個 signal悉盆,在傳入的 didSubscribe block 里面,首先讓源信號和 subject 建立聯(lián)系馋吗,然后讓訂閱者訂閱這個 subject焕盟。

replayLazily 很明顯和之前的兩個不同的是,只有第一次新的 subscriber 訂閱源 signal 時宏粤,內(nèi)部的 subject 才會和源 signal 建立聯(lián)系脚翘,didSubscribe 才會被調(diào)用,值才會被保存下來并且發(fā)送出去绍哎,而之后的訂閱會因?yàn)橐呀?jīng)建立聯(lián)系来农,_hasConnected 值為 1,所以不會再調(diào)用源信號的 subscribe崇堰,而是直接發(fā)送已經(jīng)保存的值沃于。

這樣解釋下來感覺挺亂的,關(guān)于它們?nèi)齻€的區(qū)別海诲,如果想看比較好的形式化的解釋繁莹,請移步看這篇文章

總的來說特幔,RACMulticastConnection 是為了實(shí)現(xiàn)多次訂閱而只存在一次實(shí)際訂閱過程而創(chuàng)造的類咨演,底部的實(shí)現(xiàn)就是用一個 subject 來訂閱源信號,然后將這個 subject 作為信號暴露出去供其他訂閱者訂閱蚯斯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雪标,一起剝皮案震驚了整個濱河市零院,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌村刨,老刑警劉巖告抄,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嵌牺,居然都是意外死亡打洼,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門逆粹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來募疮,“玉大人,你說我怎么就攤上這事僻弹“⑴ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵蹋绽,是天一觀的道長芭毙。 經(jīng)常有香客問我,道長卸耘,這世上最難降的妖魔是什么退敦? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蚣抗,結(jié)果婚禮上侈百,老公的妹妹穿的比我還像新娘。我一直安慰自己翰铡,他們只是感情好钝域,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锭魔,像睡著了一般例证。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赂毯,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天战虏,我揣著相機(jī)與錄音拣宰,去河邊找鬼党涕。 笑死,一個胖子當(dāng)著我的面吹牛巡社,可吹牛的內(nèi)容都是我干的膛堤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼晌该,長吁一口氣:“原來是場噩夢啊……” “哼肥荔!你這毒婦竟也來了绿渣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤燕耿,失蹤者是張志新(化名)和其女友劉穎中符,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體誉帅,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淀散,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚜锨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片档插。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖亚再,靈堂內(nèi)的尸體忽然破棺而出郭膛,到底是詐尸還是另有隱情,我是刑警寧澤氛悬,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布则剃,位于F島的核電站,受9級特大地震影響圆雁,放射性物質(zhì)發(fā)生泄漏忍级。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一伪朽、第九天 我趴在偏房一處隱蔽的房頂上張望轴咱。 院中可真熱鬧,春花似錦烈涮、人聲如沸朴肺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戈稿。三九已至,卻和暖如春讶舰,著一層夾襖步出監(jiān)牢的瞬間鞍盗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工跳昼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留般甲,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓鹅颊,卻偏偏與公主長得像敷存,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子堪伍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容