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

參考:http://www.itdecent.cn/p/4fee21fb05b3

我們在領略到RAC的強大和不可思議的時候,需要思考兩個地方:1、它是如何實現(xiàn)的?第二個問題則更有難度:2、它是如何想到這樣設計的? 這里我們先嘗試研究第一個問題,它是如何實現(xiàn)的,分析主要的脈絡。

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

優(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什么時候調(diào)用?

?[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什么時候調(diào)用?


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

1、隱藏的訂閱者

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

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

第一步是創(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ā)現(xiàn),RACDynamicSignal有一個屬性,名字叫didSubscribe的block對象。createSignal方法傳遞的block參數(shù),就是賦值給didSubscribe屬性。

?對于問題1,我們可以暫時這么回答,createSignal的意義是,創(chuàng)建一個signal對象,并且把參數(shù)賦值給signal的名為didSubscribe的屬性,這個block的參數(shù)是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的參數(shù)傳遞給RACSubscriber對象,RACSubscriber會把參數(shù)賦值給自己一個名為next的Block類型的屬性,這里,我們可以回答上面第4個問題,subscribeNext方法創(chuàng)建一個訂閱者,并且把block參數(shù),傳遞給訂閱者一個名字叫next的屬性,block參數(shù)接收的是id類型,返回的是RACDisposable對象。接下來執(zhí)行[self subscribe:o],也就是訂閱操作。我們在看看訂閱方法subscribe的實現(xiàn):上面的代碼很清晰,直接是self.didSubscribe(subscriber),我們可以知道,剛剛創(chuàng)建的subscriber對象,直接傳遞給上文中我們提到的signal的didSubscribe屬性。這樣,我們可以解釋上面的第二個和第三個問題,subscriber就是didSubscribe的形參,block對象是在subscribeNext的時候執(zhí)行的,剛剛的訂閱者對象作為參數(shù)傳入,就是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ā)現(xiàn),sendNext的實現(xiàn),也就是直接執(zhí)行上文中的nextBlock。也就是回答了上面第五個問題。

?總結一下,signal持有didSubscribe參數(shù)(createSignal傳進來的那個block),subscriber持有nextBlock(就是subscribeNext傳進來的那個block),當執(zhí)行[signal subscribe:subscriber]的時候,signal的didSubscribe執(zhí)行,內(nèi)部有subscriber sendNext的調(diào)用,觸發(fā)了subscriber的nextBlock的調(diào)用。到這里,我們基本把信號和訂閱者,以及訂閱過程分析完畢。

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

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

還有就是,我們剛剛談到了三種事件,分別是next、error、complete,和分析next的訂閱過程一樣,舉個例子,我們發(fā)送網(wǎng)絡請求,希望在出錯的時候,能給用戶提示,那么首先,創(chuàng)建信號的時候,在網(wǎng)絡請求失敗的回調(diào)中,我們要[subscriber sendError:netError],也就是發(fā)送錯誤事件。然后在訂閱錯誤事件,也就是subscriberError:...這樣就完成了錯誤信號的訂閱。

complete事件比較特殊,它有終止訂閱關系的意味,我們先大致了解一下RACDispoable對象吧,我們知道,訂閱關系需要有終止的時候,比如,在tableViewCell的復用的時候,cell會訂閱model類產(chǎn)生一個信號,但是當cell被復用的時候,如果不把之前的訂閱關系取消掉,就會出現(xiàn)同時訂閱了2個model的情況。我們可以發(fā)現(xiàn),subscribeNext、subscribeError、subscribeComplete事件返回的都是RACDisopable對象,當我們希望終止訂閱的時候,調(diào)用[RACDisposable dispose]就可以了。complete也是這個原理。

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

RAC是一個非常龐大的框架,平時的一些教程會誤導大家糾結flattenMap和map的區(qū)別,這些問題,讓人找不到頭緒,導致入門更加的困難。實際上,學習它需要一個循循漸進的過程,RAC有很多作用,解耦合,更高效的解決一類問題等等,總之,他是對常規(guī)的面向對象編程很好的補充。所以,在理解完訂閱的過程之后,重要的是,投入實際的運用中,我觀察了不少開源的項目,并結合自己的實踐發(fā)現(xiàn),其實flattenMap這樣的操作,非常少,幾乎沒有,常用的無非就是以下幾個:手動構建信號(createSignal)、訂閱信號(subscribeNext)、使用框架的一些宏定義(RACObserve、RAC)、然后就是學習幾個最簡單的操作,例如map、merge等就可以開始了。如果希望深入研究,一定要把這些基礎的東西吃透,然后在學習更多的操作,例如flattenMap,了解side effect和多播的概念,學會RACSubject的用法(這個也是非常重要的對象)等等。如果把這些操作比作武器的話,可能更重要的是內(nèi)功,也就是理解他的思想,我們?nèi)绾瓮ㄟ^實戰(zhàn),知道恰當?shù)睦盟膹姶?,慢慢的熟悉和深入是水到渠成的事情?/p>

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容