ReactiveCocoa學(xué)習(xí)筆記整理(一)

由于近期時間相對寬裕劲件,以及很多朋友詢問關(guān)于RAC的學(xué)習(xí)路徑以及資料,故而整理一下之前自己學(xué)習(xí)RAC的學(xué)習(xí)筆記扫尺,供大家查閱練習(xí)筋栋。該文章只講解了RAC的詳細(xì)用法,有關(guān)其內(nèi)部原理以及實現(xiàn)正驻,請參考ReactiveCocoa深入理解 弊攘。

一. ReactiveCocoa簡介

1.解決的問題

作為一個iOS開發(fā)者,你寫的每一行代碼幾乎都是在相應(yīng)某個事件姑曙,例如按鈕的點擊襟交,收到網(wǎng)絡(luò)消息,屬性的變化(通過KVO)或者用戶位置的變化(通過CoreLocation)伤靠。但是這些事件都用不同的方式來處理捣域,比如action、delegate宴合、KVO焕梅、callback等。ReactiveCocoa為事件定義了一個標(biāo)準(zhǔn)接口形纺,從而可以使用一些基本工具來更容易的連接丘侠、過濾和組合徒欣。

2.編程思想

ReactiveCocoa結(jié)合了幾種編程風(fēng)格:

  • 函數(shù)式編程(Functional Programming)

  • 響應(yīng)式編程(Reactive Programming)

所以逐样,你可能聽說過ReactiveCocoa被描述為函數(shù)響應(yīng)式編程(FRP)框架。以后使用RAC解決問題,就不需要考慮調(diào)用順序脂新,直接考慮結(jié)果挪捕,把每一次操作都寫成一系列嵌套的方法中,使代碼高聚合争便,方便管理级零。

3.常見的五個宏

  • RAC(TARGET, [KEYPATH, [NIL_VALUE]])

作用: 用于給某個對象的某個屬性綁定

實例: 只要文本框的文字改變,就會修改label的文字

RAC(self.labelView,text) = _textField.rac_textSignal;

  • RACObserve(self, name)

作用: 監(jiān)聽某個對象的某個屬性滞乙,返回的是信號

實例: 監(jiān)聽self.view的center變化

[RACObserve(self.view, center) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];

注意事項: 當(dāng)RACObserve放在block里面使用時一定要加上weakify奏纪,不管里面有沒有使用到self;否則會內(nèi)存泄漏斩启,因為RACObserve宏里面就有一個self序调。

@weakify(self);
RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) {
   Avoids a retain cycle because of RACObserve implicitly referencing self
  @strongify(self);
  return RACObserve(arrayController, items);
}];
  • @weakify(Obj)和@strongify(Obj)

一般兩個都是配套使用,在主頭文件(ReactiveCocoa.h)中并沒有導(dǎo)入,需要自己手動導(dǎo)入兔簇,RACEXTScope.h才可以使用发绢。但是每次導(dǎo)入都非常麻煩,只需要在主頭文件自己導(dǎo)入就好了

  • RACTuplePack

作用: 把數(shù)據(jù)包裝成RACTuple(元組類)

實例: 把參數(shù)中的數(shù)據(jù)包裝成元組

RACTuple *tuple = RACTuplePack(@10,@20);

  • RACTupleUnpack

作用: 把RACTuple(元組類)解包成對應(yīng)的數(shù)據(jù)

實例: 把參數(shù)中的數(shù)據(jù)包裝成元組

RACTuple *tuple = RACTuplePack(@"xmg",@20);

注意事項: 解包元組垄琐,會把元組的值边酒,按順序給參數(shù)里面的變量賦值

二. RACSignal基礎(chǔ)知識點

1.信號類(RACSignal) 簡介

信號類(RACSiganl),只是表示當(dāng)數(shù)據(jù)改變時狸窘,信號內(nèi)部會發(fā)出數(shù)據(jù)墩朦,它本身不具備發(fā)送信號的能力,而是交給內(nèi)部一個訂閱者去發(fā)出.默認(rèn)一個信號都是冷信號朦前,也就是值改變了介杆,也不會觸發(fā),只有訂閱了這個信號韭寸,這個信號才會變?yōu)闊嵝盘柎荷冢蹈淖兞瞬艜|發(fā)。

如何訂閱信號?

調(diào)用信號RACSignal的subscribeNext就能訂閱

2.信號類(RACSignal)實踐

1. RACSignal的簡單使用

完整的創(chuàng)建RACSignal 包含三部分sendError(不一定要有) sendNext(可多個) sendCompleted(不一定要有)恩伺。 下面代碼中的RACSubscriber表示訂閱者的意思赴背,用于發(fā)送信號,這是一個協(xié)議晶渠,不是一個類凰荚,只要遵守這個協(xié)議,并且實現(xiàn)方法才能成為訂閱者褒脯。通過create創(chuàng)建的信號便瑟,都有一個訂閱者,幫助他發(fā)送數(shù)據(jù)番川。

 RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        @strongify(self)
        NSError *error;
        if ([self.curTag isEqualToString:@"error"]) {
            error=[[NSError alloc]initWithDomain:@"myError" code:2001 userInfo:nil];
            [subscriber sendError:error];
        }
        else
        {
            [subscriber sendNext:@"1"];
            [subscriber sendNext:@"3"];
            [subscriber sendNext:@"5"];
            [subscriber sendCompleted];
        }
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"執(zhí)行清理");
            //RACDisposable:用于取消訂閱或者清理資源到涂,當(dāng)信號發(fā)送完成或者發(fā)送錯誤的時候脊框,就會自動觸發(fā)它
            //使用場景:不想監(jiān)聽某個信號時,可以通過它主動取消訂閱信號
        }];
    }];
    
    [signal subscribeNext:^(id x) {
        NSLog(@"當(dāng)前的值為:%@",x);
    }];
    
    [signal subscribeError:^(NSError *error) {
        NSLog(@"當(dāng)前出現(xiàn)錯誤%@",error);
    }];
    
    [signal subscribeNext:^(id x) {
        NSLog(@"2當(dāng)前的值為:%@",x);
    }];

以上代碼的輸出為:

    執(zhí)行清理
    當(dāng)前出現(xiàn)錯誤Error Domain=myError Code=2001 "(null)"
    執(zhí)行清理
    執(zhí)行清理

2. filter践啄、map以及flattenMap的使用

首先我們創(chuàng)建簡單的signal對象浇雹,然后通過該對象對filter、map以及flattenMap進(jìn)行講解屿讽。

 RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"3"];
        [subscriber sendNext:@"15"];
        [subscriber sendNext:@"wujy"];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"執(zhí)行清理");
        }];
    }];

然后昭灵,我們看一下filter的作用,filter顧名思義就是過濾的意思伐谈,我們來看以下代碼:

 [[signal filter:^BOOL(id value) {
        if ([value isEqualToString:@"wujy"]) {
            return YES;
        }
        return NO;
    }] subscribeNext:^(id x) {
        NSLog(@"當(dāng)前的值為:%@",x);
    }];
//輸出為: 當(dāng)前的值為:wujy  執(zhí)行清理

以上的代碼邏輯中烂完,對signal進(jìn)行過濾,只有傳入的字符串為“wujy”诵棵,才發(fā)送信號窜护。

接下來,我們看一下先filter過濾后又用map進(jìn)行轉(zhuǎn)換的效果非春,請看以下代碼:

[[[signal filter:^BOOL(id value) {
        if ([value isEqualToString:@"wujy"]) {
            return NO;
        }
        return YES;
    }] map:^id(NSString *value) {
        return @(value.length);
    }] subscribeNext:^(NSNumber *x) {
        NSLog(@"當(dāng)前的位數(shù)為:%zd",[x integerValue]);
    }];
//輸出為: 當(dāng)前的位數(shù)為:1  
         當(dāng)前的位數(shù)為:1     
         當(dāng)前的位數(shù)為:2    
         執(zhí)行清理

用map后原來的值就被轉(zhuǎn)化柱徙,上述代碼中,將value的值轉(zhuǎn)化成了長度奇昙。

接下來护侮,我們再看一下flattenMap的使用效果,代碼如下:

 [[signal flattenMap:^RACStream *(id value) {
        return [RACSignal return:[NSString stringWithFormat:@"當(dāng)前輸出為:%@",value]];
    }] subscribeNext:^(id x) {
        NSLog(@"flattenMap中執(zhí)行:%@",x);
    }];
flattenMap的作用是 根據(jù)前一個信號的參數(shù)創(chuàng)建一個新的信號储耐,以上代碼的輸出為:  
flattenMap中執(zhí)行:當(dāng)前輸出為:1
flattenMap中執(zhí)行:當(dāng)前輸出為:3
flattenMap中執(zhí)行:當(dāng)前輸出為:15
flattenMap中執(zhí)行:當(dāng)前輸出為:wujy

那么map跟flattenMap有什么區(qū)別呢羊初?

1. FlatternMap中的Block返回信號
2. Map中的Block返回對象
3. 如果信號發(fā)出的值不是信號,映射一般使用Map
4. 如果信號發(fā)出的值是信號什湘,映射一般使用FlatternMap

3. ignore长赞、ignoreValues、take闽撤、takeUntilBlock以及takeLast的使用

之后得哆,我們再繼續(xù)介紹RAC的其他基本知識點,同樣的方式哟旗,我們通過代碼的方式呈現(xiàn)贩据。
首先,看一下ignore的使用闸餐,該用法相對簡單饱亮,我就直接上代碼了。

 //ignore 忽略某個值
    [[signal ignore:@"3"] subscribeNext:^(id x) {
        NSLog(@"當(dāng)前的值為:%@",x);
    }];
    //輸出:當(dāng)前的值為:1  當(dāng)前的值為:15  當(dāng)前的值為:wujy   執(zhí)行清理

接下來舍沙,我們看一下ignoreValues的使用效果近上,ignoreValues 這個比較極端,忽略所有值拂铡,只關(guān)心Signal結(jié)束壹无,也就是只取Comletion和Error兩個消息歼跟,中間所有值都丟棄

[[signal ignoreValues] subscribeNext:^(id x) {
        //它是沒機(jī)會執(zhí)行  因為ignoreValues已經(jīng)忽略所有的next值
        NSLog(@"ignoreValues當(dāng)前值:%@",x);
    } error:^(NSError *error) {
        NSLog(@"ignoreValues error");
    } completed:^{
        NSLog(@"ignoreValues completed");
    }];
//    輸出
//    ignoreValues completed

然后格遭,我們看一下take的相關(guān)用法。首先留瞳,我們先登場的是take的簡單使用拒迅,take的意思是:從開始一共取N次的信號

[[signal take:1] subscribeNext:^(id x) {
        NSLog(@"take 獲取的值:%@",x);
    }];
    //輸出:take 獲取的值:1

用法相對簡單她倘,不做過多介紹璧微,我們馬上來看一下takeUntilBlock的用法,takeUntilBlock的意思是:對于每個next值硬梁,運行block前硫,當(dāng)block返回YES時停止取值

 [[signal takeUntilBlock:^BOOL(NSString *x) {
        if ([x isEqualToString:@"15"]) {
            return YES;
        }
        return NO;
    }] subscribeNext:^(id x) {
        NSLog(@"takeUntilBlock 獲取的值:%@",x);
    }];
//    輸出
//    takeUntilBlock 獲取的值:1
//    takeUntilBlock 獲取的值:3

最后,我們看一下takeLast的使用荧止,takeLast的意思是: 取最后N次的信號屹电,但是它有一個前提條件,訂閱者必須調(diào)用完成跃巡,因為只有完成危号,才知道總共有多少信號。

  [[signal takeLast:1] subscribeNext:^(id x) {
        NSLog(@"takeLast 獲取的值:%@",x);
    }];
    //輸出:takeLast 獲取的值:wujy

4. skip素邪、skipUntilBlock外莲、skipWhileBlock、startWith以及reduceEach的使用

RAC的基礎(chǔ)知識真是眾多且難記兔朦,喝杯小茶偷线,我們接著來。啦啦啦沽甥,我們再看一下關(guān)于剩余的知識點声邦。首先,skip登場摆舟,skip的字面意思即跳躍翔忽,他的使用就是跳過幾個信號,不接受。skipUntilBlock的意思類似盏檐,skipUntilBlock是一直跳歇式,直到block為YES。

//skip
[[signal skip:2] subscribeNext:^(id x) {
        NSLog(@"skip 獲取的值:%@",x);
    }];
    //輸出:skip 獲取的值:15    skip 獲取的值:wujy

//skipUntilBlock
 [[signal skipUntilBlock:^BOOL(NSString *x) {
        if ([x isEqualToString:@"15"]) {
            return YES;
        }
        return NO;
    }] subscribeNext:^(id x) {
        NSLog(@"skipUntilBlock 獲取的值:%@",x);
    }];
//    輸出
//    skipUntilBlock 獲取的值:15
//    skipUntilBlock 獲取的值:wujy

skipWhileBlock跟skipUntilBlock是相反的意思胡野,skipWhileBlock的意思是一直跳材失,直到block為NO

[[signal skipWhileBlock:^BOOL(NSString *x) {
        if ([x isEqualToString:@"15"]) {
            return NO;
        }
        return YES;
    }] subscribeNext:^(id x) {
        NSLog(@"skipWhileBlock 獲取的值:%@",x);
    }];
//    輸出
//    skipWhileBlock 獲取的值:15
//    skipWhileBlock 獲取的值:wujy

接下來,我們看一下startWith以及reduceEach的用法硫豆,startWith表示起始位置增加相應(yīng)的元素龙巨,不要跟字符串的拼接混用笼呆,跟字符串的拼接還是有區(qū)別的。

 RACSignal *addStartSignal=[RACSignal return:@"123"];
    [[addStartSignal startWith:@"345"] subscribeNext:^(id x) {
        NSLog(@"startWith增加的值操作 %@",x);
    }];
//    輸出
//    startWith增加的值操作 345
//    startWith增加的值操作 123

reduceEach的意思是聚合,用于信號發(fā)出的內(nèi)容是元組旨别,把信號發(fā)出元組的值聚合成一個值诗赌。

 RACSignal *aSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:RACTuplePack(@1,@4)];
        [subscriber sendNext:RACTuplePack(@2,@3)];
        [subscriber sendNext:RACTuplePack(@5,@2)];
        return nil;
    }];
    
    [[aSignal reduceEach:^id(NSNumber *first,NSNumber *secnod){
        return @([first integerValue]+[secnod integerValue]);
    }] subscribeNext:^(NSNumber *x) {
        NSLog(@"reduceEach當(dāng)前的值:%zd",[x integerValue]);
    }];
//    輸出
//    reduceEach當(dāng)前的值:5
//    reduceEach當(dāng)前的值:5
//    reduceEach當(dāng)前的值:7

5. 關(guān)于時間以及流程相關(guān)的RAC語法操作

說到時間,首先想到的就是計時器的使用場景秸弛,在RAC中也有對應(yīng)的計時器操作API铭若,即: (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler的用法。模仿定時器用递览,我們設(shè)置take方式定拟,每隔段時間發(fā)出一個信號丝格。

 [[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] take:5]subscribeNext:^(id x) {
        NSLog(@"interval-take :吃藥");
    }];
    //    輸出(每隔一秒執(zhí)行一句)
    //    interval-take :吃藥
    //    interval-take :吃藥
    //    interval-take :吃藥
    //    interval-take :吃藥
    //    interval-take :吃藥

接下來朴下,我們看一下有關(guān)超時的操作流程囱嫩,請看以下代碼:

 [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
        [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
            NSLog(@"我快到了");
            [subscriber sendNext:nil];
            [subscriber sendCompleted];
            return nil;
            //延遲2秒后執(zhí)行next事件
        }] delay:2] subscribeNext:^(id x) {
            NSLog(@"我到了");
            [subscriber sendNext:nil];
            [subscriber sendCompleted];
        }];
        return nil;
    }] timeout:1 onScheduler:[RACScheduler mainThreadScheduler]] subscribeError:^(NSError *error) {
        NSLog(@"你再不來,我走了");
    }];
    
    //輸出
    //我快到了
    //你再不來儿捧,我走了
    //我到了

在我們的實際開發(fā)中荚坞,有很多場景需要我們重新執(zhí)行某個操作,比如網(wǎng)絡(luò)請求中的再次刷新等菲盾,類似的場景我們就可以使用retry語法來實現(xiàn)西剥。若發(fā)送的是error則可以使用retry來嘗試重新刺激信號 retry重試 :只要失敗,就會重新執(zhí)行創(chuàng)建信號中的block,直到成功.

 [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        NSLog(@"i = %d",i);
        if (i == 5) {
            [subscriber sendNext:@"i == 2"];
        }else{
            i ++;
            [subscriber sendError:nil];
        }
        return nil;
        //當(dāng)發(fā)送的是error時可以retry重新執(zhí)行
    }] retry] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    //    輸出:
    //    i = 0
    //    i = 1
    //    i = 2
    //    i = 3
    //    i = 4
    //    i = 5
    //    i == 2

還有一種我們常用的場景亿汞,即我們需要達(dá)到某個條件時瞭空,停止某些操作。如通知的注銷疗我,計時器的銷毀等咆畏,我們可以使用takeUntil更加方便快捷的實現(xiàn)該需求。

    //創(chuàng)建一個信號
    [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
        //創(chuàng)建一個定時信號吴裤,每隔1秒刺激一次信號
        [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
            [subscriber sendNext:@"直到世界的盡頭才能把我們分開"];
        }];
        return nil;
        //直到此情況下停止刺激信號
    }] takeUntil:[RACSignal createSignal:^RACDisposable *(id subscriber) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"世界的盡頭到了");
            [subscriber sendNext:@"世界的盡頭到了"];
        });
        return nil;
    }]] subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
    //輸出:
    //    直到世界的盡頭才能把我們分開
    //    直到世界的盡頭才能把我們分開
    //    直到世界的盡頭才能把我們分開
    //    世界的盡頭到了

最后旧找,再介紹一下doNext跟doCompleted以及throttle的使用,doNext: 執(zhí)行Next之前麦牺,會先執(zhí)行這個Block; doCompleted: 執(zhí)行sendCompleted之前钮蛛,會先執(zhí)行這個Block。先看一下doNext跟doCompleted的使用:

[[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"執(zhí)行sendNext"];
        NSLog(@"執(zhí)行sendNext");
        [subscriber sendCompleted];
        return nil;
    }] doNext:^(id x) {
        NSLog(@"執(zhí)行doNext");
    }] doCompleted:^{
        NSLog(@"執(zhí)行doCompleted");
    }] subscribeNext:^(id x) {
        NSLog(@"執(zhí)行subscribeNext");
    }];
    
//    輸出
//    執(zhí)行doNext
//    執(zhí)行subscribeNext
//    執(zhí)行sendNext
//    執(zhí)行doCompleted

throttle是節(jié)流的意思剖膳,用來處理當(dāng)某個信號發(fā)送比較頻繁的情況魏颓。該情況下可以使用節(jié)流,在某一段時間不發(fā)送信號內(nèi)容吱晒,過了一段時間獲取信號的最新內(nèi)容發(fā)出甸饱。

    RACSubject *throttleSignal = [RACSubject subject];
    [throttleSignal sendNext:@"throttle a"];
    // 節(jié)流,在一定時間(4秒)內(nèi),不接收任何信號內(nèi)容叹话,過了這個時間(1秒)獲取最后發(fā)送的信號內(nèi)容發(fā)出偷遗。
    [[throttleSignal throttle:4] subscribeNext:^(id x) {
        NSLog(@"throttleSignal:%@",x);
    }];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"世界的盡頭到了");
         [throttleSignal sendNext:@"throttle b"];
         [throttleSignal sendNext:@"throttle c"];
    });
    //輸出:throttleSignal:throttle c

6. combineLatest、reduce驼壶、then氏豌、aggregateWithStart、scanWithStart的使用

在此之前热凹,我們大多介紹的是基于單個信號的操作泵喘,接下來,我們講解一下關(guān)于信號與信號之間的操作與處理問題碌嘀。我們需要創(chuàng)建兩個簡單的信號,然后基于這兩個信號歪架,我們進(jìn)行接下來的講解工作股冗。

 RACSignal *aSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@"3"];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"aSignal清理了");
        }];
    }];
    
    RACSignal *bSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"7"];
        [subscriber sendNext:@"9"];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"bSignal清理了");
        }];
    }];

首先,看一下combineLatest的用法和蚪,他的作用是將多個信號合并起來止状,并且拿到各個信號的最新的值, combineLatest有一個前提條件攒霹,即:必須每個合并的signal至少都有過一次sendNext怯疤,才會觸發(fā)合并的信號。

    RACSignal *combineSignal = [aSignal combineLatestWith:bSignal];
    
    [combineSignal subscribeNext:^(id x) {
        
        NSLog(@"combineSignal為:%@",x);
    }];
    //輸出
//    combineSignal為:<RACTuple: 0x600000015c30> (
//                                               3,
//                                               7
//                                               )
//    combineSignal為:<RACTuple: 0x600000015cb0> (
//                                                3,
//                                                9
//                                                                                                  )

combineLatest一般結(jié)合reduce聚合一起使用催束,將產(chǎn)生的最新的值聚合在一起集峦,并生成一個新的信號。

 RACSignal *combineReduceSignal=[RACSignal combineLatest:@[aSignal,bSignal] reduce:^id(NSString *aItem,NSString *bItem){
        return [NSString stringWithFormat:@"%@-%@",aItem,bItem];
    }];
    
    [combineReduceSignal subscribeNext:^(id x) {
        NSLog(@"合并后combineSignal的值:%@",x);
    }];
    //輸出:aSignal清理了   合并后combineSignal的值:3-7    合并后combineSignal的值:3-9   bSignal清理了

從結(jié)果可以看出此種合并會將第一個信號中最后一個sendnext與后面信號的所有sendnext結(jié)合起來作為一個數(shù)組抠刺,而next觸發(fā)次數(shù)以bSignal中的next次數(shù)為主塔淤。

然后,介紹一下then的用法速妖,用于連接兩個信號高蜂,當(dāng)?shù)谝粋€信號完成,才會連接then返回的信號罕容。

  RACSignal *thenSignal=[aSignal then:^RACSignal *{
        return bSignal;
    }];
    
    [thenSignal subscribeNext:^(id x) {
        NSLog(@"thenSignal的值:%@",x);
    }];
    //輸出  thenSignal的值:7   thenSignal的值:9   bSignal清理了  aSignal清理了

then可以用來處理串行的需求备恤,就像一下實例:

 [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"第一步");
        [subscriber sendCompleted];
        return nil;
    }] then:^RACSignal *{
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"第二步");
            [subscriber sendCompleted];
            return nil;
        }];
    }] then:^RACSignal *{
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"第三步");
            return nil;
        }];
    }] subscribeCompleted:^{
        NSLog(@"完成");
    }];
    //輸出:第一步   第二步  第三步

最后,我們看一下aggregateWithStart跟scanWithStart的使用锦秒,aggregateWithStart的意思是 從哪個位置開始 進(jìn)行順序兩值進(jìn)行操作 最后只有一個被操作后的值露泊,而scanWithStart的意思是 從哪個位置開始 然后每個位置跟前面的值進(jìn)行操作 它會有根據(jù)NEXT的個數(shù)來顯示對應(yīng)的值。我們通過具體的代碼來看:

    RACSignal *operateSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@2];
        [subscriber sendNext:@12];
        [subscriber sendNext:@15];
        [subscriber sendCompleted];
        return nil;
    }];
    
    
    //aggregateWithStar
    [[operateSignal aggregateWithStart:@0 reduce:^id(NSNumber *running, NSNumber *next) {
        return @([running integerValue]+[next integerValue]);
    }] subscribeNext:^(id x) {
        NSLog(@"aggregateWithStart 當(dāng)前值:%@",x);
    }];
    //輸出
    //aggregateWithStart 當(dāng)前值:29
    
    
    //scanWithStart 
    [[operateSignal scanWithStart:@0 reduce:^id(NSNumber *running, NSNumber *next) {
        return @([running integerValue]+[next integerValue]);
    }] subscribeNext:^(id x) {
        NSLog(@"scanWithStart 當(dāng)前值:%@",x);
    }];
    //輸出
    //scanWithStart 當(dāng)前值:2
    //scanWithStart 當(dāng)前值:14
    //scanWithStart 當(dāng)前值:29

7. 信號隊列的使用

信號隊列顧名思義就是將一組信號排成隊列旅择,挨個調(diào)用滤淳。Talk is cheap, show you the code .

    //創(chuàng)建3個信號來模擬隊列
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        [subscriber sendNext:@"喜歡一個人"];
        [subscriber sendCompleted];
        return nil;
    }];
    RACSignal *signalC = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        [subscriber sendNext:@"直接去表白"];
        [subscriber sendCompleted];
        return nil;
    }];
    RACSignal *signalD = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        [subscriber sendNext:@"成功在一起"];
        [subscriber sendCompleted];
        return nil;
    }];
    
    RACSignal *signalGroup = [[signalB concat:signalC] concat:signalD];
    [signalGroup subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    //輸出:喜歡一個人   直接去表白   成功在一起

以上的代碼中,我們使用了concat來連接組隊列,concat將幾個信號放進(jìn)一個組里面,按順序連接每個,每個信號必須執(zhí)行sendCompleted方法后才能執(zhí)行下一個信號砌左。此處脖咐,我們也可以用merge來處理,merge用來合并隊列铺敌。

 [[RACSignal merge:@[signalB,signalC,signalD]] subscribeNext:^(id x) {
        NSLog(@"merge:%@",x);
    }];
    //輸出:merge:喜歡一個人   merge:直接去表白   merge:成功在一起

concat跟merge的區(qū)別是什么呢?
concat每個信號必須執(zhí)行sendCompleted方法后才能執(zhí)行下一個信號,而merge不用屁擅。

最后的最后偿凭,我們看一下信號的壓縮zipWith的用法,壓縮具有一一對應(yīng)關(guān)系,以2個信號中 消息發(fā)送數(shù)量少的為主對應(yīng)派歌。

RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        [subscriber sendNext:@"我想你"];
        [subscriber sendNext:@"我不想你"];
        [subscriber sendNext:@"Test"];
        return nil;
    }];
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        [subscriber sendNext:@"嗯"];
        [subscriber sendNext:@"你豁我"];
        return nil;
    }];
  
    [[signalA zipWith:signalB] subscribeNext:^(RACTuple* x) {
        //解包RACTuple中的對象
        RACTupleUnpack(NSString *stringA, NSString *stringB) = x;
        NSLog(@"%@%@", stringA, stringB);
    }];
    //輸出:我想你  嗯   我不想你  你豁我

若將此結(jié)果于合并作對比弯囊,我們可以發(fā)現(xiàn)他們只是觸發(fā)next事件的次數(shù)所關(guān)聯(lián)對象不一樣,是以信號中next事件數(shù)量較少的為主胶果。
發(fā)現(xiàn)已經(jīng)不知不覺介紹了很多了匾嘱,那就先這樣吧,剩下的內(nèi)容我們下篇文章再聊早抠,歡迎你前來圍觀霎烙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蕊连,隨后出現(xiàn)的幾起案子悬垃,更是在濱河造成了極大的恐慌,老刑警劉巖甘苍,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尝蠕,死亡現(xiàn)場離奇詭異,居然都是意外死亡载庭,警方通過查閱死者的電腦和手機(jī)看彼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囚聚,“玉大人闲昭,你說我怎么就攤上這事∶一樱” “怎么了序矩?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長跋破。 經(jīng)常有香客問我簸淀,道長,這世上最難降的妖魔是什么毒返? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任租幕,我火速辦了婚禮,結(jié)果婚禮上拧簸,老公的妹妹穿的比我還像新娘劲绪。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布贾富。 她就那樣靜靜地躺著歉眷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颤枪。 梳的紋絲不亂的頭發(fā)上汗捡,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音畏纲,去河邊找鬼扇住。 笑死,一個胖子當(dāng)著我的面吹牛盗胀,可吹牛的內(nèi)容都是我干的艘蹋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼票灰,長吁一口氣:“原來是場噩夢啊……” “哼女阀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起米间,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤强品,失蹤者是張志新(化名)和其女友劉穎膘侮,沒想到半個月后屈糊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡琼了,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年逻锐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雕薪。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡昧诱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出所袁,到底是詐尸還是另有隱情盏档,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布燥爷,位于F島的核電站蜈亩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏前翎。R本人自食惡果不足惜稚配,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望港华。 院中可真熱鬧道川,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宦言,卻和暖如春扇单,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奠旺。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工蜘澜, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人响疚。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓鄙信,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忿晕。 傳聞我的和親對象是個殘疾皇子装诡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容