簡介
ReactiveCocoa 在GitHub有1.5萬多個星,不少大型公司的的都用它作為主流框架,比如美團,但它同時又是一個非常復雜的框架,在正式開始介紹它的核心組件前,我們先來看看它的類圖,以便從宏觀上了解它的層次結構:

從上面的類圖中,我們可以看出,ReactiveCocoa 主要由以下四大核心組件構成:
- 信號源:RACStream 及其子類;
- 訂閱者:RACSubscriber 的實現(xiàn)類及其子類;
- 調(diào)度器:RACScheduler 及其子類;
- 清潔工:RACDisposable 及其子類。
ReactiveCocoa作用
在我們iOS開發(fā)過程中,當某些事件響應的時候,需要處理某些業(yè)務邏輯,這些事件都用不同的方式來處理。
比如按鈕的點擊使用action,ScrollView滾動使用delegate,屬性值改變使用KVO等系統(tǒng)提供的方式。
其實這些事件,都可以通過RAC處理
ReactiveCocoa為事件提供了很多處理方法,而且利用RAC處理事件很方便,可以把要處理的事情,和監(jiān)聽的事情的代碼放在一起,這樣非常方便我們管理,就不需要跳到對應的方法里。非常符合我們開發(fā)中高聚合,低耦合的思想
對于一個應用來說,絕大部分的時間都是在等待某些事件的發(fā)生或響應某些狀態(tài)的變化,比如用戶的觸摸事件、應用進入后臺、網(wǎng)絡請求成功刷新界面等等,而維護這些狀態(tài)的變化,常常會使代碼變得非常復雜,難以擴展。而 ReactiveCocoa 給出了一種非常好的解決方案,它使用信號來代表這些異步事件,提供了一種統(tǒng)一的方式來處理所有異步的行為,包括代理方法、block 回調(diào)、target-action 機制、通知、KVO 等:
// 代理方法
[[self
rac_signalForSelector:@selector(webViewDidStartLoad:)
fromProtocol:@protocol(UIWebViewDelegate)]
subscribeNext:^(id x) {
// 實現(xiàn) webViewDidStartLoad: 代理方法
}];
// target-action
[[self.avatarButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(UIButton *avatarButton) {
// avatarButton 被點擊了
}];
// 通知
[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:kReachabilityChangedNotification object:nil]
subscribeNext:^(NSNotification *notification) {
// 收到 kReachabilityChangedNotification 通知
}];
// KVO
[RACObserve(self, username) subscribeNext:^(NSString *username) {
// 用戶名發(fā)生了變化
}];
然而,這些還只是 ReactiveCocoa 的冰山一角,它真正強大的地方在于我們可以對這些不同的信號進行任意地組合和鏈式操作,從最原始的輸入 input 開始直至得到最終的輸出 output 為止:
[[[RACSignal
combineLatest:@[ RACObserve(self, username), RACObserve(self, password) ]
reduce:^(NSString *username, NSString *password) {
return @(username.length > 0 && password.length > 0);
}]
distinctUntilChanged]
subscribeNext:^(NSNumber *valid) {
if (valid.boolValue) {
// 用戶名和密碼合法,登錄按鈕可用
} else {
// 用戶名或密碼不合法,登錄按鈕不可用
}
}];
RACSignal(信號源)
RACSignal 代表的是未來將會被傳送的值,它是一種 push-driven 的流。RACSignal 可以向訂閱者發(fā)送三種不同類型的事件:
-
next :RACSignal通過next事件向訂閱者傳送新的值,并且這個值可以為 nil ; -
error :RACSignal通過error事件向訂閱者表明信號在正常結束前發(fā)生了錯誤; -
completed :RACSignal通過completed事件向訂閱者表明信號已經(jīng)正常結束,不會再有后續(xù)的值傳送給訂閱者。
注意,ReactiveCocoa中的值流只包含正常的值,即通過 next 事件傳送的值,并不包括 error 和 completed 事件,它們需要被特殊處理。通常情況下,一個信號的生命周期是由任意個 next 事件和一個 error 事件或一個 completed 事件組成的
RACSubscriber(訂閱者)
現(xiàn)在,我們已經(jīng)知道信號源是什么了,為了獲取信號源中的值,我們需要對信號源進行訂閱。在 ReactiveCocoa 中,訂閱者是一個抽象的概念,所有實現(xiàn)了 RACSubscriber 協(xié)議的類都可以作為信號源的訂閱者
其中 -sendNext: 、-sendError: 和 -sendCompleted 分別用來從 RACSignal 接收 next 、error 和 completed 事件,而 -didSubscribeWithDisposable: 則用來接收代表某次訂閱的 disposable 對象。
訂閱者對信號源的一次訂閱過程可以抽象為:通過 RACSignal 的 -subscribe: 方法傳入一個訂閱者,并最終返回一個 RACDisposable 對象的過程:

)
RACScheduler(調(diào)度器)
有了信號源和訂閱者,我們還需要由調(diào)度器來統(tǒng)一調(diào)度訂閱者訂閱信號源的過程中所涉及到的任務,這樣才能保證所有的任務都能夠合理有序地執(zhí)行
RACScheduler 在 ReactiveCocoa 中就是扮演著調(diào)度器的角色,本質(zhì)上,它就是用 GCD 的串行隊列來實現(xiàn)的,并且支持取消操作。是的,在 ReactiveCocoa 中,并沒有使用到 NSOperationQueue 和 NSRunloop 等技術,RACScheduler 也只是對 GCD 的簡單封裝而已
RACDisposable(清潔工)
正如我們前面所說的,在訂閱者訂閱信號源的過程中,可能會產(chǎn)生副作用或者消耗一定的資源,所以當我們在取消訂閱或者完成訂閱時,我們就需要做一些資源回收和垃圾清理的工作
RACDisposable 在 ReactiveCocoa 中就充當著清潔工的角色,它封裝了取消和清理一次訂閱所必需的工作。它有一個核心的方法 -dispose ,調(diào)用這個方法就會執(zhí)行相應的清理工作,這有點類似于 NSObject 的 -dealloc 方法。RACDisposable 總共有四個子類,它的繼承結構圖如下

-
RACSerialDisposable:作為disposable的容器使用,可以包含一個disposable對象,并且允許將這個disposable對象通過原子操作交換出來; -
RACKVOTrampoline:代表一次 KVO 觀察,并且可以用來停止觀察; -
RACCompoundDisposable:跟RACSerialDisposable一樣,*RACCompoundDisposable也是作為disposable的容器使用。不同的是,它可以包含多個disposable對象,并且支持手動添加和移除disposable對象,有點類似于可變數(shù)組NSMutableArray。而當一個RACCompoundDisposable對象被disposed時,它會調(diào)用其所包含的所有disposable對象的 -dispose方法,有點類似于autoreleasepool的作用; -
RACScopedDisposable:當它被dealloc的時候調(diào)用本身的-dispose方法
=========基礎知識解釋==========
-
RACSubject:RACSubject:信號提供者,自己可以充當信號,又能發(fā)送信號。
使用場景:通常用來代替代理,有了它,就不必要定義代理了。
RACReplaySubject:重復提供信號類,RACSubject的子類。
RACReplaySubject與RACSubject區(qū)別:
RACReplaySubject可以先發(fā)送信號,在訂閱信號,RACSubject就不可以。
使用場景一:如果一個信號每被訂閱一次,就需要把之前的值重復發(fā)送一遍,使用重復提供信號類。
使用場景二:可以設置capacity數(shù)量來限制緩存的value的數(shù)量,即只緩充最新的幾個值。
RACTuple:元組類,類似NSArray,用來包裝值RACSequence:RAC中的集合類,用于代替NSArray,NSDictionary,可以使用它來快速遍歷數(shù)組和字典RACCommand:RAC中用于處理事件的類,可以把事件如何處理,事件中的數(shù)據(jù)如何傳遞,包裝到這個類中,他可以很方便的監(jiān)控事件的執(zhí)行過程。
- 使用場景:監(jiān)聽按鈕點擊,網(wǎng)絡請求
RACScheduler:RAC中的隊列,用GCD封裝的代替代理:
rac_signalForSelector:用于替代代理。代替KVO :
rac_valuesAndChangesForKeyPath:用于監(jiān)聽某個對象的屬性改變。監(jiān)聽事件:
rac_signalForControlEvents:用于監(jiān)聽某個事件。代替通知:
rac_addObserverForName:用于監(jiān)聽某個通知。監(jiān)聽文本框文字改變:
rac_textSignal:只要文本框發(fā)出改變就會發(fā)出這個信號。處理當界面有多次請求時,需要都獲取到數(shù)據(jù)時,才能展示界面
rac_liftSelector:withSignalsFromArray:Signals:當傳入的Signals(信號數(shù)組),每一個signal都至少sendNext過一次,就會去觸發(fā)第一個selector參數(shù)的方法。
使用注意:幾個信號,參數(shù)一的方法就幾個參數(shù),每個參數(shù)對應信號發(fā)出的數(shù)據(jù)。
操作符用法解釋
一. flattenMap 和 map的區(qū)別
Map:用于把原信號中的內(nèi)容映射成新的內(nèi)容
flattenMap的作用:把原信號的內(nèi)容映射成一個新的信號,信號可以是任意的類型</br>
1.FlatternMap中的Block返回信號
2.Map中的Block返回對象
3.開發(fā)中,如果信號發(fā)出的值不是信號,映射一般使用Map
4.開發(fā)中,如果信號發(fā)出的值是信號,映射一般使用FlatternMap
RACSequence* numbers =self.dataArrNumber.rac_sequence;
// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
RACSequence* extended = [numbers flattenMap:^RACSequence *(NSString* num) {
return@[num,num].rac_sequence;
}];
[[extended signal] subscribeNext:^(NSString* text) {
[self logSource:@"extended" text:text];
}];
flatten
就像一條流水線,將源信號一個一個發(fā)出去,也可以理解成把幾個水管的龍頭合并成一個,按照順序連接
contact
按一定順序拼接信號,當多個信號發(fā)出的時候有順序的接受信號
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACSubject subject];
//把subjectA拼接到subjectB的時候只有subjectA發(fā)送完畢之后subjectB才會被激活
// 只需要訂閱拼接之后的信號,不在需要單獨拼接subjectA或者subjectB,內(nèi)部會自動訂閱
[[subjectA concat:subjectB] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subjectA sendNext:@"subjectA發(fā)送完信號"];
// 第一個信號發(fā)送完成,第二個信號才會被激活
[subjectA sendCompleted];
[subjectB sendNext:@"subjectB發(fā)送完信號”];
then
then:用于連接兩個信號,當?shù)谝粋€信號完成才會連接then返回的信號
使用then之前的信號會被忽略掉
merge
把多個信號合并為一個信號,任何一個信號有新值時就會調(diào)用
zipWith
把兩個信號壓縮成一個信號,只有當兩個信號同時發(fā)出信號內(nèi)容的時候,并且把兩個信號的內(nèi)容合并成一個元組,才會觸發(fā)壓縮流的next事件
reduce
聚合:用于信號發(fā)出的內(nèi)容是元組,把信號發(fā)出元組的值聚合成一個值。
fiter
使用提供的block來覺得事件是否往下傳遞
combineLatest:reduce
組合源信號數(shù)組中的信號,并生成一個新的信號。每次源信號數(shù)組中的一個輸出新值時,reduce塊都會被執(zhí)行,而返回的值會作為組合信號的下一個值。
doNext
附加操作,并不返回一個值
參考文檔:http://m.itdecent.cn/p/87ef6720a096
http://blog.leichunfeng.com/blog/2015/12/25/reactivecocoa-v2-dot-5-yuan-ma-jie-xi-zhi-jia-gou-zong-lan/