訊號的使用,包括:建立訊號、傳送訊號、訂閱訊號
建立訊號
獲取訊號的方式有很多種:
建立單元訊號
建立動態訊號
通過Cocoa橋接
從別的訊號變換而來
由序列變換而來
單元訊號
最簡單的訊號是單元訊號,有4種:
// return訊號:被訂閱後,馬上產生一個值事件,同時產生一個完成事件
RACSignal *signal1 = [RACSignal return:someObject];
// error訊號:被訂閱後,馬上產生一個錯誤事件,同時產生一個完成事件
RACSignal *signal2 = [RACSignal error:someError];
// empty訊號:被訂閱後,馬上產生一個空事件,同時產生一個完成事件
RACSignal *signal3 = [RACSignal empty];
// never訊號:永遠不產生事件,同時產生一個完成事件
RACSignal *signal4 = [RACSignal never]; 複製程式碼
動態訊號
RACSignal *signal5 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
}];
}];複製程式碼
Cocoa橋接
RAC為大量的Cocoa型別提供便捷的訊號橋接工具,如下是一些常見的橋接方式:
RACSignal *signal6 = [object rac_signalForSelector:@selector(setFrame:)];
RACSignal *signal7 = [control rac_signalForControlEvents:UIControlEventTouchUpInside];
RACSignal *signal8 = [object rac_willDeallocSignal];
RACSignal *signal9 = RACObserve(object, keyPath);複製程式碼
訊號變換
RACSignal *signal10 = [signal1 map:^id(id value) {
return someObject;
}];複製程式碼
序列變換
RACSignal *signal11 = sequence.signal;複製程式碼
訂閱訊號
訂閱訊號的方式有3種:
通過
subscribeNext:error:completed:
方法訂閱RAC巨集繫結
Cocoa橋接
subscribeNext:error:completed:
是最基礎的訊號訂閱方法;
RAC巨集繫結 可以使用RAC()
巨集 如:RAC(view, backgroundColor) = signal;
Cocoa橋接 [object rac_liftSelector:@selector(someSelector:) withSignals:signal1, signal2, nil];
訂閱過程
所謂訂閱過程指的是訊號被訂閱的處理邏輯,如下是簡單的例子:
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"dispose"); // 當錯誤事件或者完成事件產生時,該block被呼叫
}];
}];
[signal subscribeNext:^(id x) {
NSLog(@"next value is : %@", x);
} error:^(NSError *error) {
NSLog(@"error : %@", error);
} completed:^{
NSLog(@"completed");
}];複製程式碼
訊號的各類操作
RACTuple , 就像NSArray陣列:
RACTuple *tuple = RACTuplePack(@1, @"haha");
id first = tuple.first;
id second = tuple.second;
id last = tuple.last;
id index1 = tuple[1];
RACTupleUnpack(NSNumber *num, NSString *str) = tuple;
複製程式碼
訊號的操作一般分為單個訊號的變換、多個訊號的組合及高階操作
單個訊號的變換
分為值操作、數量操作、時間操作、副作用操作
值操作
map 做一個對映,返回新的訊號
mapReplace 等價於返回同一個值得map
reduceEach,是map:的變體。當signalA的值事件包裹的資料是RACTuple型別時,才可以使用該操作,將RACTuple組合成一個
reduceApply,要求值事件包裹的資料型別是RACTuple,並且該RACTuple的第一個元素是一個block,後面的元素作為該block的引數傳入,返回該block的執行結果。
邏輯操作:not, and, or
數量操作
filter 過濾
ignore 忽略
take 只取前n個
skip 過濾掉前n個
startWith 從某個元素開始
distinctUntilChanged 去重複
repeat 重複
retry [signalA retry:2]; 重試幾次
collect 把所以集合到一個
aggregate 和sacn 類似但有區別 scan 實時輸出,aggregate 最終結束才輸出,出錯則不輸出
副作用操作
doNext doError doCompleted
initially finally
時間操作
delay 延遲
throttle:1 當signalA事件流產生一個值事件時,若1 s內沒有其他的值事件產生,則signalB事件流中也會產生該值事件;當signalA事件流產生一個值事件時,若1s內有其他的值事件產生,則signalB會過濾掉該值事件,對於signalA中的最後一個值事件,signalB事件流中總會也包含它
多個訊號的組合
concat 加在後面
merge 插入進來
zip 兩個訊號合成一個RACTuple
combineLatest a訊號來一個值事件就和b訊號最近的值事件組合
sample 類似取景和快門
takeUntil signalC事件流中的事件和signalA一一對應,直到signalB事件流中出現了訊號,事件流就終結。
訊號的高階操作
switchToLatest 讀到新的訊號就把之前的訊號關閉
if then else 其實就是switchToLatest的實現
+ (RACSignal *)if:(RACSignal *)boolSignal
then:(RACSignal *)trueSignal
else:(RACSignal *)falseSignal {
.....
return [[[boolSignal map:^(NSNumber *value) {
NSCAssert([value isKindOfClass:NSNumber.class], @"Expected %@ to send BOOLs, not %@", boolSignal, value);
return (value.boolValue ? trueSignal : falseSignal);
}] switchToLatest]
setNameWithFormat:@"+if: %@ then: %@ else: %@", boolSignal, trueSignal, falseSignal];
}複製程式碼
代替代理
rac_signalForSelector
用於代替代理
代替 KVO
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
:節流,當某個訊號傳送比較頻繁時,可以使用節流,在某一段時間不傳送訊號內容,過了一段時間獲取訊號的最新內容發出。
ReactiveCocoa 常見巨集
RAC(TARGET, ...)
用於繫結某個物件的某個屬性RACObserve(TARGET, KEYPATH)
用於監聽某個物件的某個屬性,返回的是訊號@weakify(Obj)
&@strongify(Obj)
配套使用