An abstract class representing any stream of values.編程
RACStream
是一個抽象類,是以 Monad
(函數式編程語言)的概念爲依據進行設計的,它表明的就是一個 Monad
。有了 Monad
做爲基石後,許多基於流的操做就能夠被創建起來了,好比map
、 filter
、 zip
等。緩存
能夠把信號想象成水龍頭,只不過裏面不是水,而是玻璃球(value),直徑跟水管的內徑同樣,這樣就能保證玻璃球是依次排列,不會出現並排的狀況(數據都是線性處理的,不會出現併發狀況)。水龍頭的開關默認是關的,除非有了接收方(subscriber),纔會打開。這樣只要有新的玻璃球進來,就會自動傳送給接收方。能夠在水龍頭上加一個過濾嘴(filter),不符合的不讓經過,也能夠加一個改動裝置,把球改變成符合本身的需求(map)。也能夠把多個水龍頭合併成一個新的水龍頭(combineLatest:reduce:),這樣只要其中的一個水龍頭有玻璃球出來,這個新合併的水龍頭就會獲得這個球。閉包
RACSignal
表明的是未來會被傳遞的值,它是一種 push-driven
的流。 RACSignal
能夠向訂閱者發送三種不一樣類型的事件:併發
next
: RACSignal
經過 next
事件向訂閱者傳送新的值,而且這個值能夠爲 nil
;error
: RACSignal
經過 error
事件向訂閱者代表信號在正常結束前發生了錯誤;completed
: RACSignal
經過 completed
事件向訂閱者代表信號已經正常結束,不會再有後續的值傳送給訂閱者。注意, ReactiveCocoa
中的值流只包含正常的值,即經過 next
事件傳送的值,並不包括 error
和 completed
事件,它們須要被特殊處理。一般狀況下,一個信號的生命週期是由任意個next
事件和一個 error
事件或一個 completed
事件組成的。less
RACSignal
的 Subscription
過程歸納起來能夠分爲三個步驟:編程語言
RACSubject
表明的是能夠手動控制的信號,咱們能夠把它看做是 RACSignal
的可變版本,就比如 NSMutableArray
是 NSArray
的可變版本同樣。 RACSubject
繼承自 RACSignal
,因此它能夠做爲信號源被訂閱者訂閱,同時,它又實現了 RACSubscriber
協議,因此它也能夠做爲訂閱者訂閱其餘信號源,這個就是 RACSubject
爲何能夠手動控制的緣由。ide
實際使用中,在 MVVM
中使用 RACSubject
能夠很是方便地實現統一的錯誤處理邏輯。好比,咱們能夠在 viewModel
的基類中聲明一個 RACSubject
類型的屬性 errors
,而後在 viewController
的基類中編寫統一的錯誤處理邏輯:函數式編程
[self.viewModel.errors subscribeNext:^(NSError *error) {
// 錯誤處理邏輯
}
A command is a signal triggered in response to some action, typically UI-related.函數
RACCommand
一般用來表示某個 Action
的執行,好比點擊 Button
。它有幾個比較重要的屬性:executionSignals / errors / executing。oop
executionSignals
:是 signal of signals ,若是直接 subscribe 的話會獲得一個 signal ,而不是咱們想要的 value,因此通常會配合 switchToLatest
。errors
:跟正常的 signal 不同,RACCommand 的錯誤不是經過 sendError 來實現的,而是經過 errors 屬性傳遞出來的。executing
:表示該 command 當前是否正在執行。Represents an immutable sequence of values. Unless otherwise specified, the sequences’ values are evaluated lazily on demand. Like Cocoa collections, sequences cannot contain nil.
RACSequence
表明的是一個不可變的值的序列,與 RACSignal
不一樣,它是 pull-driven
類型的流。從嚴格意義上講, RACSequence
並不能算做是信號源,由於它並不能像 RACSignal
那樣,能夠被訂閱者訂閱,可是它與 RACSignal
之間能夠很是方便地進行轉換。
所以,咱們能夠很是方便地使用 RACSequence
來實現集合的鏈式操做,直到獲得你想要的最終結果爲止,經常使用的使用場景爲「字典轉模型」。
注意: RACSequence
會涉及到性能與效率的問題。
Represents any object which can directly receive values from a RACSignal.
訂閱者對信號源的一次訂閱過程能夠抽象爲:經過 RACSignal
的 -subscribe:
方法傳入一個訂閱者,並最終返回一個 RACDisposable
對象的過程。
注意:在 ReactiveCocoa
中並無專門的類 RACSubscription
來表明一次訂閱,而間接地使用 RACDisposable
來充當這一角色。所以,一個 RACDisposable
對象就表明着一次訂閱,而且咱們能夠用它來取消此次訂閱。
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock { trueNSCParameterAssert(nextBlock != NULL); true trueRACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL]; truereturn [self subscribe:o]; } + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed { trueRACSubscriber *subscriber = [[self alloc] init]; truesubscriber->_next = [next copy]; truesubscriber->_error = [error copy]; truesubscriber->_completed = [completed copy]; truereturn subscriber; } - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber { trueNSCAssert(NO, @"This method must be overridden by subclasses"); truereturn nil; }
Schedulers are used to control when and where work is performed.
RACScheduler
在 ReactiveCocoa
中就是扮演着調度器的角色,本質上,它就是用 GCD
的串行隊列來實現的,而且支持取消操做。是的,在 ReactiveCocoa
中,並無使用到 NSOperationQueue
和 NSRunloop
等技術, RACScheduler
也只是對 GCD
的簡單封裝而已。
A disposable encapsulates the work necessary to tear down and cleanup a subscription.
RACDisposable
在 ReactiveCocoa
中就充當着清潔工的角色,它封裝了取消和清理一次訂閱所必需的工做。它有一個核心的方法 -dispose
,調用這個方法就會執行相應的清理工做,這有點相似於 NSObject
的 -dealloc
方法。
rac_signalForSelector
用戶代替代理rac_valuesAndChangesForKeyPath
用於監聽某個對象的某個屬性的改變rac_signalForControlEvents
用於監聽某個事件rac_addObserverForName
用於監聽某個通知,且不須要在 - (void)dealloc
中移除監聽rac_textSignal
用於監聽文本框文字變化rac_gestureSignal
用於監聽手勢操做rac_liftSelector:withSignalsFromArray:Signals
當傳入的 Signals,每個 Signal 都至少 sendNext 過一次,就會去觸發第一個 selector 參數的方法。bind
:函數會返回一個新的信號 N。總體思路是對原信號 O 進行訂閱,每當信號 O 產生一個值就將其轉變成一箇中間信號 M ,並立刻訂閱 M ,以後將信號M的輸出做爲新信號 N 的輸出。map
\ flattenMap
:用於把源信號內容映射成新的內容(信號)。concat
:組合,按必定順序拼接信號,當多個信號發出的時候,有順序的接收信號。then
:用於鏈接兩個信號,當第一個信號完成,纔會鏈接 then
返回的信號。merge
:把多個信號合併爲一個信號,任何一個信號有新值的時候就會調用。zipWith
:把兩個信號壓縮成一個信號,只有當兩個信號同時發出信號內容時,而且把兩個信號的內容合併成一個元組,纔會觸發壓縮流的 next
事件。combineLatest
:將多個信號合併起來,而且拿到各個信號的最新的值,必須每一個合併的 signal
至少都有過一次 sendNext
,纔會觸發合併的信號。reduce
:聚合,用於信號發出的內容是元組,把信號發出元組的值聚合成一個值。filter
:過濾信號,使用它能夠獲取知足條件的信號。ignore
:忽略某些值的信號,使用 RACObserve
時可配合使用,其實現由 filter
完成。distinctUntilChanged
:實現是用 bind
來完成的,每次變換中都記錄一下原信號上一次發送過來的值,並與這一次進行比較,若是是相同的值,就「吞」掉,返回 empty 信號。只有和原信號上一次發送的值不一樣,變換後的新信號才把這個值發送出來。take
:從開始一共取 N 次的信號。takeLast
:取最後 N 次的信號,前提條件:訂閱者必須調用完成,由於只有完成,才知道總共有多少信號。takeUntil
:獲取信號直到某個信號執行完成。skip
:跳過幾個信號,不接受。switchToLatest
:用於 signalOfSignals
(信號的信號),有時候信號也會發出信號,會在 signalOfSignals
中,獲取 signalOfSignals
發送的最新信號。doNext
:執行 next
以前,會先執行這個 Block 。doCompleted
:執行 sendCompleted
以前,會先執行這個Block 。timeout
:超時,可讓一個信號在必定的時間後,自動報錯。interval
:定時:每隔一段時間發出信號。delay
:延遲發送 next
。retry
:重試,只要失敗,就會從新執行建立信號中的 block ,直到成功。replay
:重放,當一個信號被屢次訂閱,反覆播放內容。throttle
:節流,當某個信號發送比較頻繁時,可使用節流,在某一段時間不發送信號內容,過了一段時間獲取信號的最新內容發出。RAC(TARGET, ...)
用於綁定某個對象的某個屬性RACObserve(TARGET, KEYPATH)
用於監聽某個對象的某個屬性,返回的是信號@weakify(Obj)
& @strongify(Obj)
配套使用Side effects occur for each subscription by default, but there are certain situations where side effects should only occur once – for example, a network request typically should not be repeated when a new subscriber is added.
若是某個信號被多個 subscriber
訂閱,那麼它的 didSubscribe
會被屢次調用。
若是想要避免這種狀況的發生,可使用 reply
/ replayLast
/ replayLazily
方法,它們的做用是保證 signal
只被觸發一次,而後把 sendNext:
的 value
給緩存起來,下一次再有新的 subscriber
時,直接發送緩存的 value
。
其內部實現依賴: - (RACMulticastConnection *)multicast:(RACSubject *)subject;
這個方法。
RAC 給 UITableViewCell
提供了一個方法: rac_prepareForReuseSignal
,它的做用是當 Cell 即將要被重用時,告訴 Cell 。想象 Cell 上有多個 Button
,Cell 在初始化時給每一個 Button 都 addTarget:action:forControlEvents
,被重用時須要先移除這些 target ,下面這段代碼就能夠很方便地解決這個問題:
[[[self.cancelButton
truerac_signalForControlEvents:UIControlEventTouchUpInside]
truetakeUntil:self.rac_prepareForReuseSignal]
truesubscribeNext:^(UIButton *x) {
true// do other things }];
由於 RAC 不少操做都是在 Block
中進行的,因此最多見的問題即是「循環引用」,因此須要經過 @weakify
和 @strongify
來消除循環引用。
注意:事實上 RACObserve(TARGET, KEYPATH)
老是會引用 self
,即便 target 不是 self ,因此只要有 RACObserve 的地方都要使用 @weakify
/ @strongify
。
flattenMap
中的 block 返回信號。map
中的 block 返回對象。map
的實現是用了 flattenMap
函數來實現的。把 map
的入參閉包,放到了 flattenMap
的返回值中。map
。flattenMap
。signalOfsignals
用 flattenMap
。Subject
相似「直播」,錯過了就再也不處理。而 Signal
相似「點播」。RACSubject
及其子類是熱信號, RACSignal
排除 RACSubject
類之外的是冷信號。RACSubject
會持有訂閱者(由於 RACSubject
是熱信號,爲了保證將來有事件發送的時候,訂閱者能夠收到信息,因此須要對訂閱者保持狀態,作法就是持有訂閱者),而 RACSignal
不會持有訂閱者。當一個 signal
被一個 subscriber
subscribe 後,這個 subscriber
什麼時候會被移除?答案是:當 subscriber
被 sendComplete
或 sendError
時,或者手動調用 [disposable dispose]
。
replay
是 multicast
的一個特殊 case 而已。
當 subscriber
被 dispose
後,全部該 subscriber
相關的工做都會被中止或取消,如 http 請求,資源也會被釋放。
Errors
有優先權,若是有多個 signals
被同時監聽,只要其中一個 signal sendError
,那麼 error 就會馬上被傳送給 subscriber ,並致使 signals 終止執行。至關於 Exception
。
使用 RACSubject
,若是進行了 map
操做,那麼必定要發送完成信號,否則會內存泄漏。
任何的信號轉換便是對原有的信號進行訂閱從而產生新的信號。