簡介
ReactiveCocoa
在GitHub有1.5萬多個星恤浪,不少大型公司的的都用它作為主流框架,比如美團蒙保,但它同時又是一個非常復雜的框架蠢古,在正式開始介紹它的核心組件前奴曙,我們先來看看它的類圖,以便從宏觀上了解它的層次結(jié)構(gòu):
從上面的類圖中草讶,我們可以看出洽糟,ReactiveCocoa 主要由以下四大核心組件構(gòu)成:
- 信號源: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
事件向訂閱者表明信號在正常結(jié)束前發(fā)生了錯誤; -
completed :RACSignal
通過completed
事件向訂閱者表明信號已經(jīng)正常結(jié)束感帅,不會再有后續(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
等技術(shù)摆舟,RACScheduler
也只是對 GCD 的簡單封裝而已
RACDisposable(清潔工)
正如我們前面所說的亥曹,在訂閱者訂閱信號源的過程中邓了,可能會產(chǎn)生副作用或者消耗一定的資源,所以當我們在取消訂閱或者完成訂閱時媳瞪,我們就需要做一些資源回收和垃圾清理的工作
RACDisposable
在 ReactiveCocoa
中就充當著清潔工的角色骗炉,它封裝了取消和清理一次訂閱所必需的工作。它有一個核心的方法 -dispose
蛇受,調(diào)用這個方法就會執(zhí)行相應的清理工作句葵,這有點類似于 NSObject
的 -dealloc
方法。RACDisposable
總共有四個子類兢仰,它的繼承結(jié)構(gòu)圖如下
-
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://www.reibang.com/p/87ef6720a096
http://blog.leichunfeng.com/blog/2015/12/25/reactivecocoa-v2-dot-5-yuan-ma-jie-xi-zhi-jia-gou-zong-lan/