RACSignal分析

monigsjbdbsjhdbh發表於2019-01-01

訊號的使用,包括:建立訊號、傳送訊號、訂閱訊號

建立訊號

獲取訊號的方式有很多種:

  • 建立單元訊號

  • 建立動態訊號

  • 通過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) 配套使用


相關文章