ReactiveCocoa代碼實踐之-更多思考

三.ReactiveCocoa代碼實踐之-更多思考

1. RACObserve()宏形參寫法的區別

以前寫代碼考慮過 RACObserve(self.timeLabel , text) 和 RACObserve(self , timeLabel.text) 的區別。 由於這兩種方法都是觀察self.timeLabel.text的屬性,而且都能實現功能。估計是做者本來用的其中一種後來對另外一種也提供了支持,究竟有什麼區別哪種寫法更好?java

點進去看RACObserve的源碼 大多都是方法調用,一層一層點進去最後來到這個方法。 編程

- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)weakObserver block:(void (^)(id, NSDictionary *, BOOL, BOOL))block

這個方法裏面把逗號後面的keypath經過「.」 進行分割成了一個數組。 而且獲得三個屬性數組

BOOL keyPathHasOneComponent = (keyPathComponents.count == 1);
NSString *keyPathHead = keyPathComponents[0];
NSString *keyPathTail = keyPath.rac_keyPathByDeletingFirstKeyPathComponent;

這裏會取到keypatch的頭和去掉頭的部分,而且會在下面方法內部本身調用本身 app

// Adds the callback block to the remaining path components on the value. Also
// adds the logic to clean up the callbacks to the firstComponentDisposable.
void (^addObserverToValue)(NSObject *) = ^(NSObject *value) {
    RACDisposable *observerDisposable = [value rac_observeKeyPath:keyPathTail options:(options & ~NSKeyValueObservingOptionInitial) observer:weakObserver block:block];
    [firstComponentDisposable() addDisposable:observerDisposable];
};
 

而且把keyPathTail 做爲keypatch傳進去了,就是遞歸調用,每一次進來都會切掉第一個元素,直到BOOL keyPathHasOneComponent 這個值等於yes。從這個角度看用RACObserve(self , timeLabel.text) 這種寫法會引起遞歸調用,性能不如RACObserve(self.timeLabel.text)。框架

更多RAC宏相關知識可見這篇 :http://blog.sunnyxx.com/2014/03/06/rac_1_macros/ide

 

2.集合操做

假設如今有一個需求,有一串密碼的數組,咱們判斷密碼長度小於6位就是過短,就會系統內部拋出一個消息:XXX密碼過短不合格。採用RAC的寫法會比常規寫法方便,一個過濾一個自定義而後直接返回。函數式編程

NSArray *pwds = @[@"567887",@"89877",@"789",@"7899000"];
RACSequence *results = [[pwds.rac_sequence
                 filter:^ BOOL (NSString *pwd) {
                     return pwd.length < 6;
                 }]map:^id(NSString *pwd) {
                     return [[pwd mutableCopy]stringByAppendingString:@"密碼過短不合格"];
                 }];
NSLog(@"%@",results.array);

中間filter方法的block內代碼會在下面results.array代碼執行時纔會執行, 至關因而有了個訂閱者纔會執行。這一點和RACSignal很像,由於signal 和 sequence 都是streams,他們共享不少相同的方法signal是push驅動的stream,sequence是pull驅動的stream。函數

若是相從RACSequence對象中取出其餘屬性時進行操做也能夠用以下方法性能

RACSequence *s = [RACSequence sequenceWithHeadBlock:^id{
    return @"自定義操做";
} tailBlock:^RACSequence *{
    return [RACSequence new];
}];
NSLog(@"%@",s.head);
NSLog(@"%@",s.tail);

兩個block分別會在指定屬性被調用時纔會執行,注意head就是sequence的第一個元素,而tail是除去第一個元素的剩餘全部,因此仍是一個sequence。(董鉑然博客園)學習

 

3.信號實現遊戲技能釋放

假設如今須要用RAC模擬一個街機裏放爆氣技能的方法。 按下了指定的按鈕順序下前下前拳就會釋放絕招。

首先須要將各個按鈕連線,並設置一個信號來監聽全部按鍵單獨信號的並集,捕捉到每一個按鈕的title。

// 把六個按鍵的信號合併
RACSignal *comboSignal = [[RACSignal merge:@[
     [self.topBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
     [self.bottomBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
     [self.leftBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
     [self.rightBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
     [self.BBtn rac_signalForControlEvents:UIControlEventTouchUpInside],
     [self.ABtn rac_signalForControlEvents:UIControlEventTouchUpInside]]]
map:^id(UIButton *btn) {
      return btn.currentTitle;
}];

而後對這個信號源進行buffer操做,把每三秒收到的全部按鍵信息都捕獲到,並進行判斷和後繼操做

// 設置觸發爆氣條件
NSString *comboCode = @"下前下前拳";
// 實際操做
RACSignal *canAction = [[[comboSignal bufferWithTime:3 onScheduler:[RACScheduler mainThreadScheduler]] map:^id(RACTuple *value) {
    return [[value allObjects] componentsJoinedByString:@""];
}] map:^id(NSString *value) {
    return @([value containsString:comboCode]);
}];
// 調用combo:方法就是技能釋放
[self rac_liftSelector:@selector(combo:) withSignalsFromArray:@[canAction]];

上面的代碼能夠實現預計的功能,只要你能在三秒的buffer內按出指定的按鍵就能釋放。可是用這個方法中間也有一個問題:設置了buffer3秒後這個block裏面每隔三秒纔會來到一次,也就是說若是你在0.5秒內就按出了技能,那也須要再等2.5秒才能放出技能,顯然這個在實戰中是不能接受的。

因而嘗試了其餘的實現思路,嘗試了takeLast:及takeUntilBlock:及scanWithStart: 等方法都不是很合適,最後使用了aggregateWithStart:  達到了需求的目的。

// 設置觸發爆氣條件
NSString *comboCode = @"下前下前拳";
// 實際操做
_time = [[[NSDate alloc] init] timeIntervalSince1970];
[[comboSignal aggregateWithStart:@"" reduce:^id(NSString* running, NSString* next) {
    if (([[[NSDate alloc] init] timeIntervalSince1970] - _time) < 3){
        NSString *str = [NSString stringWithFormat:@"%@%@",running,next];
        return [str containsString:comboCode]?[self combo]:str;
    }
    _time = [[[NSDate alloc] init] timeIntervalSince1970];
    return str.length < combo.length ? str : [str subStringFromIndex:str.length - comboCode.length];
}]subscribeNext:^(id x){
}];

使用這段代碼能夠在知足以前條件的前提下,而且按鈕一按完立刻觸發技能。

aggregateWithStart:reduce:的第一個參數是初始值,第二個參數是一個block,這個block的返回值就是下一次來到這個block的 running參數。我在這個block的循環中作的操做有:

1.對時間進行delta計算,若是距離上一次時間節點大於3秒,刷新時間節點從新計時。 str小於5則返回,大於5則截取後五位返回。

2.若是小於3秒則把每次按鍵信息聚合成一個字符串並判斷是否包含技能觸發代碼。

3.知足的話觸發技能,技能方法的內部也刷新了時間節點,並截取running(保留最後4位,防止上一個循環結束和下一個循環開始所知足的條件)。不知足則將這個字符串繼續返回。

雖然代碼寫的不是很好看,可是功能是實現了,感受有點彆扭,由於函數式編程倡導的是引用透明無反作用,因此上面須要記錄值和成員變量的作法很明顯就不適合用RAC了,應該還會有更好的方法實現。

 

4.其餘RAC操做

1)映射:flattenMap,Map用於把源信號內容映射成新的內容

2)組合:concat:按必定順序拼接信號,當多個信號發出的時候,有順序的接收信號

3)`then`:用於鏈接兩個信號,當第一個信號完成,纔會鏈接then返回的信號

4)`merge`:把多個信號合併爲一個信號,任何一個信號有新值的時候就會調用

5)`combineLatest`:將多個信號合併起來,而且拿到各個信號的最新的值,必須每一個合併的signal至少都有過一次sendNext,纔會觸發合併的信號。

6)`reduce`聚合:用於信號發出的內容是元組,把信號發出元組的值聚合成一個值

7)filter:過濾信號,使用它能夠獲取知足條件的信號.

8) ignore:忽略完某些值的信號.

9) distinctUntilChanged:當上一次的值和當前的值有明顯的變化就會發出信號,不然會被忽略掉

10) take:從開始一共取N次的信號

11)takeLast:取最後N次的信號,前提條件,訂閱者必須調用完成,由於只有完成,就知道總共有多少信號

12)takeUntil:(RACSignal *):獲取信號直到某個信號執行完成

13)skip:(NSUInteger):跳過幾個信號,不接受

14)switchToLatest:用於signalOfSignals(信號的信號),有時候信號也會發出信號,會在signalOfSignals中,獲取signalOfSignals發送的最新信號

15)doNext: 執行Next以前,會先執行這個Block

16)doCompleted: 執行sendCompleted以前,會先執行這個Block

17)deliverOn: 內容傳遞切換到制定線程中,反作用在原來線程中,把在建立信號時block中的代碼稱之爲反作用

18)subscribeOn: 內容傳遞和反作用都會切換到制定線程中

19)interval 定時:每隔一段時間發出信號

20)delay 延遲發送next。

21) 代替代理:

  • rac_signalForSelector:用於替代代理。

22) 代替KVO :

  • rac_valuesAndChangesForKeyPath:用於監聽某個對象的屬性改變。

23) 監聽事件:

  • rac_signalForControlEvents:用於監聽某個事件。

24) 代替通知:

  • rac_addObserverForName:用於監聽某個通知。

25) 監聽文本框文字改變:

  • rac_textSignal:只要文本框發出改變就會發出這個信號。

26) 處理當界面有屢次請求時,須要都獲取到數據時,才能展現界面

  • rac_liftSelector:withSignalsFromArray:Signals:當傳入的Signals(信號數組),每個signal都至少sendNext過一次,就會去觸發第一個selector參數的方法。
  • 使用注意:幾個信號,參數一的方法就幾個參數,每一個參數對應信號發出的數據

 

RAC曾經被冠以 學習成本搞,可讀性差,debug的噩夢等不良評價,但隨着近幾年的演變已逐漸被企業級項目所接受,而且成爲函數響應式編程主流框架。RAC用人愈來愈多,隨筆和博客也愈來愈多,學習的門檻已經大大下降。 而且我以爲初學者沒有必要一開始就把全部操做和概念都弄懂,能夠從簡單的用法開始一步步的接觸高階語法,這樣會更容易接受。

相關文章
相關標籤/搜索