參考: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)精算,知道恰當的利用他的強大瓢宦,慢慢的熟悉和深入是水到渠成的事情。