6.RAC之組合信號

RAC的組合信號有以下幾種

- (RACSignal *)concat:(RACSignal *)signal
- (RACSignal *)then:(RACSignal * (^)(void))block
- (RACSignal *)merge:(RACSignal *)signal
- (RACSignal *)zipWith:(RACSignal *)signal
+ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock

1. concat

concat組合信號,讓信號按照順序去執(zhí)行。
假如我們現(xiàn)在有這么一個(gè)需求:有兩個(gè)網(wǎng)絡(luò)請求,我們需要先請求第一個(gè),拿到第一個(gè)返回的數(shù)據(jù)后,再去執(zhí)行第二個(gè)網(wǎng)絡(luò)請求。這時(shí),我們就可以用到concat了。

// 創(chuàng)建信號A
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        NSLog(@"發(fā)送請求A");
        [subscriber sendNext:@"發(fā)送請求A返回的數(shù)據(jù)”];
        //一定要執(zhí)行sendCompleted,不然永遠(yuǎn)都不會(huì)執(zhí)行請求B
        [subscriber sendCompleted];
        return nil;
    }];
// 創(chuàng)建信號B
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        NSLog(@"發(fā)送請求B");
        [subscriber sendNext:@"發(fā)送請求B返回的數(shù)據(jù)"];
        [subscriber sendCompleted]; 
        return nil;
    }];
// concat:按照順序去連接,先執(zhí)行請求A,再去執(zhí)行請求B
    RACSignal *concatSignal = [signal concat:signalB];
// 訂閱組合信號
      [concatSignal subscribeNext:^(id  _Nullable x) {
        // 既能拿到A信號的值,又能拿到B信號的值
        NSLog(@"%@",x);
    }];
打印結(jié)果:
2017-06-11 20:28:20.632 RACDemo[2497:1073327] 發(fā)送請求A
2017-06-11 20:28:20.632 RACDemo[2497:1073327] 發(fā)送請求A返回的數(shù)據(jù)
2017-06-11 20:28:20.633 RACDemo[2497:1073327] 發(fā)送請求B
2017-06-11 20:28:20.633 RACDemo[2497:1073327] 發(fā)送請求B返回的數(shù)據(jù)

上面有個(gè)要注意的點(diǎn):
當(dāng)你使用concat 連接信號的時(shí)候,第一個(gè)信號一定要執(zhí)行sendCompleted方法,不然永遠(yuǎn)不用執(zhí)行第二個(gè)信號。

2. then

then也是用于連接連個(gè)信號,當(dāng)?shù)谝粋€(gè)信號完成,才會(huì)連接then返回的信號,then底層也是使用了concat實(shí)現(xiàn)。
例子

// 創(chuàng)建信號A
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"發(fā)送請求A");
        //其實(shí)寫這句是沒用的,因?yàn)橛胻hen連接信號,會(huì)自動(dòng)忽略第一個(gè)信號的所有值
        [subscriber sendNext:@"發(fā)送請求A返回的數(shù)據(jù)”]; 
        [subscriber sendCompleted];
        return nil;
    }];
// 創(chuàng)建信號B
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"發(fā)送請求B");
        
        [subscriber sendNext:@"發(fā)送請求B返回的數(shù)據(jù)"];
        
        [subscriber sendCompleted];
        return nil;
    }];
    // 利用then組合信號
    // then:會(huì)忽略掉第一個(gè)信號的所有值
    RACSignal *thenSignal = [signal then:^RACSignal * _Nonnull{
        // 返回信號就是需要組合的信號
        return signalB;
    }];
    [thenSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
打印結(jié)果
     2017-06-05 19:49:40.045 RACDemo[4615:451292] 發(fā)送請求A
     2017-06-05 19:49:40.046 RACDemo[4615:451292] 發(fā)送請求B
     2017-06-05 19:49:40.046 RACDemo[4615:451292] 發(fā)送請求B返回的數(shù)據(jù)

then很奇葩,因?yàn)樗鼤?huì)自動(dòng)忽略掉第一個(gè)信號發(fā)送的值,只接收第二個(gè)信號發(fā)送的值。在項(xiàng)目中,基本也很少會(huì)遇見這種需求。更多使用的是concat。

3.merge

merge把兩個(gè)信號合并為一個(gè)信號,任何一個(gè)信號有新值的時(shí)候就會(huì)調(diào)用。
例子

// 創(chuàng)建信號A
    RACSubject *signalA = [RACSubject subject];
    
    // 創(chuàng)建信號B
    RACSubject *signalB = [RACSubject subject];
    
    // 組合
    RACSignal *mergeSignal = [signalA merge:signalB];

    // 訂閱信號
    [mergeSignal subscribeNext:^(id  _Nullable x) {
        // 任意一個(gè)信號發(fā)送內(nèi)容都會(huì)來到這個(gè)block里面。
        NSLog(@"%@",x);
    }];
    [signalB sendNext:@"發(fā)送B請求"];
    [signalA sendNext:@"發(fā)送A請求”];
打印結(jié)果
2017-06-12 00:13:02.364 RACDemo[3342:1458662] 發(fā)送B請求
2017-06-12 00:13:05.244 RACDemo[3342:1458662] 發(fā)送A請求

4.zipWith

把兩個(gè)信號壓縮成一個(gè)信號,只有當(dāng)兩個(gè)信號同時(shí)發(fā)出信號內(nèi)容時(shí),并且把兩個(gè)信號的內(nèi)容合并成一個(gè)元組,才會(huì)觸發(fā)事件。當(dāng)一個(gè)界面有多個(gè)網(wǎng)絡(luò)請求時(shí),要等所有請求完成才更新UI,這時(shí)我們就可以用zipWith
例子

    // 創(chuàng)建信號A
    RACSubject *signalA = [RACSubject subject];
    
    // 創(chuàng)建信號B
    RACSubject *signalB = [RACSubject subject];

    RACSignal *zipSignal = [signalA zipWith:signalB];

    // 訂閱信號
    [zipSignal subscribeNext:^(id  _Nullable x) {
        // block調(diào)用時(shí)刻:當(dāng)signalA 和 signalB 都發(fā)送信號的時(shí)候,就會(huì)調(diào)用
        // x 被包裝成一個(gè)元組
        NSLog(@"%@",x);
    }];

    [signalB sendNext:@"send B request"];
    [signalA sendNext:@"send A request"];
     打印結(jié)果:
     <RACTwoTuple: 0x60000001ae90> (
     "send A request",
     "send B request"
     )

這里我們仔細(xì)觀察,
1.zipWith 一定要兩個(gè)信號都執(zhí)行完成 sendNext 操作,才會(huì)觸發(fā)回調(diào)事件。
2.上面我們明明就是先調(diào)用 signalB 的sendNext 方法,但回調(diào)打印的卻是signalA在前面。這是為什么?因?yàn)槲覀冊诮M合信號的時(shí)候 [signalA zipWith:signalB],我們把signalA放前面,所以回調(diào)的時(shí)候它也會(huì)放在前面。

5.combineLatest

combineLatest 將多個(gè)信號合并起來,拿到各個(gè)信號的最新的值,每個(gè)合并的signal至少都有過一次sendNext,才會(huì)觸發(fā)合并的信號,底層也是用 zipWith實(shí)現(xiàn)的。 一般拿來跟 reduce 一起使用。
我們直接來看一個(gè)常見的登陸例子。
需求:一個(gè)賬號輸入框 和一個(gè)密碼 輸入框,一個(gè)登陸按鈕。當(dāng)賬號長度和密碼長度都大于0的時(shí)候,登陸按鈕可以點(diǎn)擊,否則,不可以點(diǎn)擊。

    CGFloat width = self.view.bounds.size.width;
    //賬號
    UITextField *tf1 = [[UITextField alloc]initWithFrame:CGRectMake(width / 2.0 - 100, 80, 200, 40)];
    tf1.placeholder = @"請輸入賬號";
    tf1.backgroundColor = [UIColor colorWithRed:0.0f green:0.5f blue:0.8f alpha:1.0f];
    [self.view addSubview:tf1];
    //密碼
    UITextField *tf2 = [[UITextField alloc]initWithFrame:CGRectMake(width / 2.0 - 100, 150, 200, 40)];
    tf2.placeholder = @"請輸入密碼";
    tf2.backgroundColor = [UIColor colorWithRed:0.0f green:0.5f blue:0.8f alpha:1.0f];
    [self.view addSubview:tf2];
    //登陸按鈕
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(width / 2.0 - 100, 280, 200, 40);
    [button setTitle:@"登錄" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    button.titleLabel.font = [UIFont systemFontOfSize:15];
    [button setBackgroundColor:[UIColor greenColor]];
    @weakify(self);
    [[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        @strongify(self);
        NSLog(@"登錄");
    }];
    [self.view addSubview:button];

    //組合信號
    // 第一個(gè)參數(shù):組合哪些信號
    // 第二個(gè)參數(shù):reduce 聚合
    // 注意,reduce是一個(gè)block,它看起來好像沒參數(shù),但其實(shí)它的參數(shù)是由我們自己來決定的,我們可以自己去修改
    // reduce 參數(shù): 跟組合的信號有關(guān),一一對應(yīng),你有幾個(gè)信號,就有幾個(gè)參數(shù),所以我們在下面加上(NSString *account, NSString *pwd)
    // reduce 參數(shù)類型: 組合的信號發(fā)送的是什么類型,參數(shù)就是什么類型
    RACSignal *combineSignal = [RACSignal combineLatest:@[tf1.rac_textSignal,tf2.rac_textSignal] reduce:^id _Nullable(NSString *account, NSString *pwd){
        // block調(diào)用: 只要任意一個(gè)源信號發(fā)送內(nèi)容就會(huì)調(diào)用,組合成一個(gè)新的值
        return @(account.length && pwd.length);
    }];

    [combineSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
        button.enabled = [x boolValue];
    }];

combineLatest 大多數(shù)都是和reduce一起使用,reduce使用時(shí)需要注意的就是參數(shù),上面已經(jīng)解釋的很清楚了,這里就不多余解釋了。

[combineSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
        button.enabled = [x boolValue];
    }];
//上面的寫法其實(shí)可以換成下面這種,更簡單明了
// RAC 用于給某個(gè)對象的某個(gè)屬性綁定
RAC(button,enabled) = combineSignal;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • RAC使用測試Demo下載:github.com/FuWees/WPRACTestDemo 1.ReactiveC...
    FuWees閱讀 6,641評論 3 10
  • 組合: concat組合: 按一定順序執(zhí)行皇上與皇太子關(guān)系 concat底層實(shí)現(xiàn): 1.當(dāng)拼接信號被訂閱,就會(huì)調(diào)用...
    傻傻小蘿卜閱讀 6,649評論 1 11
  • 前言由于時(shí)間的問題,暫且只更新這么多了,后續(xù)還會(huì)持續(xù)更新本文《最快讓你上手ReactiveCocoa之進(jìn)階篇》,目...
    Karos_凱閱讀 1,861評論 0 6
  • 1.ReactiveCocoa常見操作方法介紹。 1.1 ReactiveCocoa操作須知 所有的信號(RACS...
    萌芽的冬天閱讀 1,139評論 0 5
  • 致自己: 找個(gè)溫暖的人過一生吧, 即便貧窮也可以相互廝守。 可以不漂亮可以很普通但請溫柔。 可以不淵博可以不面條但...
    周末就是我閱讀 980評論 0 0

友情鏈接更多精彩內(nèi)容