RAC基礎學習一:信號和訂閱者模式

參考:http://www.reibang.com/p/4fee21fb05b3

我們在領略到RAC的強大和不可思議的時候,需要思考兩個地方:1盟榴、它是如何實現的阅虫?第二個問題則更有難度:2全谤、它是如何想到這樣設計的的猛? 這里我們先嘗試研究第一個問題,它是如何實現的氛雪,分析主要的脈絡房匆。

在RAC里面,我們所有圍繞的東西無非主體是這幾樣:信號(signal)、訂閱者(subscriber)浴鸿、還有關于信號的生產者實體井氢、信號的消費者,這幾個的關系赚楚。

優(yōu)勢:我們?yōu)槭裁词褂肦AC毙沾,因為它解耦太好了,除此之外宠页,它簡潔左胞,配合MVVM能發(fā)揮出很大的作用等等。相信我們都寫膩了對象之間的復雜通信举户、一大堆狀態(tài)的創(chuàng)建和管理烤宙、越來越難維護的業(yè)務邏輯,這些就是RAC誕生的使命俭嘁。

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber){ [subscriber sendNext:@(1)];

?[subscriber sendCompleted]; return nil; }];

1躺枕、createSignal好難啊供填;2拐云、subscriber是什么?3近她、這個block什么時候調用叉瘩?

?[signal subscribeNext:^(id x) {

if ([x boolValue]) { _navView.hidden = YES; }

else { _navView.hidden = NO;

[UIView animateWithDuration:.5 animations:^{ _navView.alpha = 1; }]; } }];

4、subscribeNext又是什么粘捎?5薇缅、這個block什么時候調用?


第一部分 訂閱者和信號###

1攒磨、隱藏的訂閱者

平時我們打交道的就是信號泳桦,但是總是說訂閱,卻不知道訂閱到底是如何進行的娩缰,也無法解答上面的問題灸撰,讓我們根據源碼分析一下訂閱過程。

首先來認識一個對象:訂閱者(RACSubscriber)拼坎。 訂閱者訂閱信號浮毯,就是這么簡單的一件事情。只不過框架隱藏了這個對象演痒,我們也不必要和訂閱者打交道,只需要告訴信號一件事情趋惨,那就是如果發(fā)送了數據(三種事件:next鸟顺、complete、error),我們需要做什么事情(類似回調的概念)讯嫂。

第一步是創(chuàng)建信號蹦锋,看一下上面的第一段代碼,createSignal類方法: 這里要說一下欧芽,信號RACSignal有一些子類莉掂,我們常用的是RACDynamicSignal和RACSubject,先不理會RACSubject千扔。createSignal類方法創(chuàng)建的就是RACDynamicSignal對象憎妙。

-----RACDynamicSignal.h-----

@property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(idsubscriber);

-----RACSignal.m-----

+ (RACSignal *)createSignal:(RACDisposable * (^)(idsubscriber))didSubscribe {

?? return [RACDynamicSignal createSignal:didSubscribe];}

-----RACDynamicSignal.m-----

+ (RACSignal *)createSignal:(RACDisposable * (^)(idsubscriber))didSubscribe {

?? RACDynamicSignal *signal = [[self alloc] init];

?? signal->_didSubscribe = [didSubscribe copy];

?? return [signal setNameWithFormat:@"+createSignal:"];}

我們可以發(fā)現,RACDynamicSignal有一個屬性曲楚,名字叫didSubscribe的block對象厘唾。createSignal方法傳遞的block參數,就是賦值給didSubscribe屬性龙誊。

?對于問題1抚垃,我們可以暫時這么回答,createSignal的意義是趟大,創(chuàng)建一個signal對象鹤树,并且把參數賦值給signal的名為didSubscribe的屬性,這個block的參數是subscriber逊朽,返回RACDisposable罕伯。

第二步是訂閱信號,看一下第二段代碼subscribeNext:

-----RACSubscriber.m-----

@property (nonatomic, copy) void (^next)(id value);

-----RACSignal.m------

?(RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {

?? NSCParameterAssert(nextBlock != NULL);

????RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];

?? return [self subscribe:o];}

-----RACDynamicSignal.m------

(RACDisposable *)subscribe:(id)subscriber {

NSCParameterAssert(subscriber != nil);

RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];

?subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

if (self.didSubscribe != NULL) {

?RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ RACDisposable *innerDisposable = self.didSubscribe(subscriber);

[disposable addDisposable:innerDisposable]; }];

?[disposable addDisposable:schedulingDisposable]; }

?return disposable; }

我們可以看到惋耙,subscribeNext方法第一步是創(chuàng)建了一個RACSubscriber捣炬,也就是創(chuàng)建了一個訂閱者,而且把subscribeNext的參數傳遞給RACSubscriber對象绽榛,RACSubscriber會把參數賦值給自己一個名為next的Block類型的屬性湿酸,這里,我們可以回答上面第4個問題灭美,subscribeNext方法創(chuàng)建一個訂閱者推溃,并且把block參數,傳遞給訂閱者一個名字叫next的屬性届腐,block參數接收的是id類型铁坎,返回的是RACDisposable對象。接下來執(zhí)行[self subscribe:o]犁苏,也就是訂閱操作硬萍。我們在看看訂閱方法subscribe的實現:上面的代碼很清晰,直接是self.didSubscribe(subscriber),我們可以知道围详,剛剛創(chuàng)建的subscriber對象朴乖,直接傳遞給上文中我們提到的signal的didSubscribe屬性祖屏。這樣,我們可以解釋上面的第二個和第三個問題买羞,subscriber就是didSubscribe的形參袁勺,block對象是在subscribeNext的時候執(zhí)行的,剛剛的訂閱者對象作為參數傳入畜普,就是subscriber對象期丰。

那么createSignal方法中,[subscriber sendNext:@(1)]是什么意思呢吃挑?

看一下sendNext方法吧:

- (void)sendNext:(id)value {

@synchronized (self) {

void (^nextBlock)(id) = [self.next copy];

if (nextBlock == nil) return;

nextBlock(value); } }

我們可以發(fā)現钝荡,sendNext的實現,也就是直接執(zhí)行上文中的nextBlock儒鹿。也就是回答了上面第五個問題化撕。

?總結一下,signal持有didSubscribe參數(createSignal傳進來的那個block)约炎,subscriber持有nextBlock(就是subscribeNext傳進來的那個block)植阴,當執(zhí)行[signal subscribe:subscriber]的時候,signal的didSubscribe執(zhí)行圾浅,內部有subscriber sendNext的調用掠手,觸發(fā)了subscriber的nextBlock的調用。到這里狸捕,我們基本把信號和訂閱者喷鸽,以及訂閱過程分析完畢。

?第二部分 信號和事件###

剛才我們說過灸拍,signal有幾個子類做祝,每一個類型的signal訂閱過程其實大同小異,而且初期常見的也就是RACDynamicSignal鸡岗,其實我們不需要太關心這個問題混槐,因為無論是自定義信號,還是框架定義的一些category轩性,例如声登,textFiled的rac_textSignal屬性,大多數都是RACDynamicSignal揣苏。另一個常見的類型RACSubject可以以后理解悯嗓。

還有就是,我們剛剛談到了三種事件卸察,分別是next脯厨、error、complete坑质,和分析next的訂閱過程一樣合武,舉個例子个少,我們發(fā)送網絡請求,希望在出錯的時候眯杏,能給用戶提示,那么首先壳澳,創(chuàng)建信號的時候岂贩,在網絡請求失敗的回調中,我們要[subscriber sendError:netError],也就是發(fā)送錯誤事件巷波。然后在訂閱錯誤事件萎津,也就是subscriberError:...這樣就完成了錯誤信號的訂閱。

complete事件比較特殊抹镊,它有終止訂閱關系的意味锉屈,我們先大致了解一下RACDispoable對象吧,我們知道垮耳,訂閱關系需要有終止的時候颈渊,比如,在tableViewCell的復用的時候终佛,cell會訂閱model類產生一個信號俊嗽,但是當cell被復用的時候,如果不把之前的訂閱關系取消掉铃彰,就會出現同時訂閱了2個model的情況绍豁。我們可以發(fā)現,subscribeNext牙捉、subscribeError竹揍、subscribeComplete事件返回的都是RACDisopable對象,當我們希望終止訂閱的時候邪铲,調用[RACDisposable dispose]就可以了芬位。complete也是這個原理。

?第三部分 進一步的深入###

RAC是一個非常龐大的框架霜浴,平時的一些教程會誤導大家糾結flattenMap和map的區(qū)別晶衷,這些問題,讓人找不到頭緒阴孟,導致入門更加的困難晌纫。實際上,學習它需要一個循循漸進的過程永丝,RAC有很多作用锹漱,解耦合,更高效的解決一類問題等等慕嚷,總之哥牍,他是對常規(guī)的面向對象編程很好的補充毕泌。所以,在理解完訂閱的過程之后嗅辣,重要的是撼泛,投入實際的運用中,我觀察了不少開源的項目澡谭,并結合自己的實踐發(fā)現愿题,其實flattenMap這樣的操作,非常少蛙奖,幾乎沒有潘酗,常用的無非就是以下幾個:手動構建信號(createSignal)、訂閱信號(subscribeNext)雁仲、使用框架的一些宏定義(RACObserve仔夺、RAC)、然后就是學習幾個最簡單的操作攒砖,例如map缸兔、merge等就可以開始了。如果希望深入研究吹艇,一定要把這些基礎的東西吃透灶体,然后在學習更多的操作,例如flattenMap掐暮,了解side effect和多播的概念蝎抽,學會RACSubject的用法(這個也是非常重要的對象)等等。如果把這些操作比作武器的話路克,可能更重要的是內功樟结,也就是理解他的思想,我們如何通過實戰(zhàn)精算,知道恰當的利用他的強大瓢宦,慢慢的熟悉和深入是水到渠成的事情。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末灰羽,一起剝皮案震驚了整個濱河市驮履,隨后出現的幾起案子,更是在濱河造成了極大的恐慌廉嚼,老刑警劉巖玫镐,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異怠噪,居然都是意外死亡恐似,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門傍念,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矫夷,“玉大人葛闷,你說我怎么就攤上這事∷海” “怎么了淑趾?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忧陪。 經常有香客問我治笨,道長,這世上最難降的妖魔是什么赤嚼? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮顺又,結果婚禮上更卒,老公的妹妹穿的比我還像新娘。我一直安慰自己稚照,他們只是感情好蹂空,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著果录,像睡著了一般上枕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弱恒,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天辨萍,我揣著相機與錄音,去河邊找鬼返弹。 笑死锈玉,一個胖子當著我的面吹牛,可吹牛的內容都是我干的义起。 我是一名探鬼主播拉背,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼默终!你這毒婦竟也來了椅棺?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤齐蔽,失蹤者是張志新(化名)和其女友劉穎两疚,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體含滴,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡鬼雀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了蛙吏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片源哩。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鞋吉,死狀恐怖,靈堂內的尸體忽然破棺而出励烦,到底是詐尸還是另有隱情谓着,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布坛掠,位于F島的核電站赊锚,受9級特大地震影響,放射性物質發(fā)生泄漏屉栓。R本人自食惡果不足惜舷蒲,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望友多。 院中可真熱鬧牲平,春花似錦、人聲如沸域滥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽启绰。三九已至昂儒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間委可,已是汗流浹背渊跋。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留着倾,地道東北人刹枉。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像屈呕,于是被迫代替她去往敵國和親微宝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容