iOS RAC 的使用總結 (轉載)

WhatsXie發表於2018-03-12

ReactiveCocoa(簡稱RAC),是GitHub上開源的一個應用於iOS和OS X開發的一個新框架.RAC具有函數語言程式設計和響應者程式設計的特性.

iOS RAC 的使用總結 (轉載)

ReactiveCocoa解決的問題:

  • 1.傳統iOS開發過程中,狀態以及狀態之間依賴過多的問題
  • 2.傳統MVC架構的問題:Controller比較複雜,可測試性差
  • 3.提供統一的訊息傳遞機制

iOS RAC 的使用總結 (轉載)

1.鍵值觀察--監聽TF的值發生變化

- (void)demo1{
    @weakify(self);
    [self.tF.rac_textSignal subscribeNext:^(NSString *value) {
        @strongify(self);
        self.value = value;
    }];

    //當self.value的值變化時呼叫Block,這是用KVO的機制,RAC封裝了KVO
    [RACObserve(self, value) subscribeNext:^(NSString *value) {
        NSLog(@"%@",value);
    }];
}
複製程式碼

2.map的使用

- (void)demo2{
  //建立一個訊號
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      //這個訊號裡面有一個Next事件的玻璃球和一個complete事件的玻璃球
        [subscriber sendNext:@"唱歌"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //對訊號進行改進,當訊號裡面流的是唱歌.就改進為'跳舞'返還給self.value
    RAC(self, tF.text) = [signalA map:^id(NSString *value) {
        if ([value isEqualToString:@"唱歌"]) {
            return @"跳舞";
        }
        return @"";
    }];     
}
複製程式碼

3.filter使用,你向西,他就向東,他向左,你就向右

- (void)demo3{
    //建立兩個通道,一個從A流出的通道A,和一個從B流出的通道B
    RACChannelTerminal *channelA = RACChannelTo(self, value);
    RACChannelTerminal *channelB = RACChannelTo(self, value2);
   
    //改造通道A,使通過通道A的值,如果等於'西',就改為'東'
    [[channelA map:^id(NSString *value) {
        if ([value isEqualToString:@"西"]) {
            NSLog(@"東");
            return @"東";
        }
        NSLog(@"====== %@",value);
        return value;
    }] subscribe:channelB];//通道A流向B
   
    //改造通道B,使通過通道B的值,如果等於'左',就改為'右'傳出去
    [[channelB map:^id(id value) {
        if ([value isEqualToString:@"左"]) {
            NSLog(@"右");
            return @"右";
        }
        NSLog(@"====== %@",value);
        return value;
    }] subscribe:channelA];//通道B流向通道A
   
    //KVO監聽valueA的值的變化,過濾valueA的值,返回Yes表示通過
    //只有value有值,才可通過
    [[RACObserve(self, value) filter:^BOOL(id value) {
        return value ? YES : NO;
    }] subscribeNext:^(id x) {
        NSLog(@"你向%@",x);
    }];
   
    //KVO監聽value2的變化
    [[RACObserve(self, value2) filter:^BOOL(id value) {
        return value ? YES: NO;
    }] subscribeNext:^(id x) {
        NSLog(@"他向%@",x);
    }];
   
    //下面使value的值和value2的值發生改變
    self.value = @"西";
    self.value2 = @"左"; 
}
複製程式碼

4.代理

1)代理的第一種寫法

.m檔案

- (void)demo4{
    [self.delegate makeAnApp:@"12345上山打老虎" String:@"老虎不在家,怎麼辦"];
}
複製程式碼

.h檔案

- (void)makeAnApp:(NSString *)string String:(NSString *)string;
@end

@interface Base5Controller : UIViewController
@property (nonatomic, assign)id<ProgrammerDelegate> delegate;
複製程式碼

第一個控制器的.h

Base5Controller *base = [[Base5Controller alloc] init];
    //  base.delegate = self;
	    [self demo4];
    // 這裡是個坑,必須將代理最後設定,否則訊號是無法訂閱到的
    // 雷純峰大大是這樣子解釋的:在設定代理的時候,系統會快取這個代理物件實現了哪些程式碼方法
    // 如果將代理放在訂閱訊號前設定,那麼當控制器成為代理時是無法快取這個代理物件實現了哪些程式碼方法的
	    base.delegate = self;
	    [self.navigationController pushViewController:base animated:YES];
    } else {
        [self.navigationController pushViewController:[cl new] animated:YES];
    }
}

#pragma mark---demo4
//使用RAC代替代理時,rac_signalForSelector: fromProtocol:這個代替代理的方法使用時,切記要將self設為代理這句話放在訂閱代理訊號的後面寫,否則會無法執行
- (void)demo4{
    //為self新增一個訊號,表示代理ProgrammerDelegate的makeAnApp;
    //RACTuple 相當於swift中的元祖
    [[self rac_signalForSelector:@selector(makeAnApp:String:) fromProtocol:@protocol(ProgrammerDelegate)] subscribeNext:^(RACTuple *x) {
        //這裡可以立即為makeAnApp的方法要執行的程式碼
        NSLog(@"%@ ",x.first);
        NSLog(@"%@",x.second);
    }];
}
複製程式碼

2)方法2 使用RACSubject替代代理

/**
*  RACSubject:訊號提供者,自己可以充當訊號,又能傳送訊號
建立方法:
1.建立RACSubject
2.訂閱訊號
3.傳送訊號
工作流程:
1.訂閱訊號,內部儲存了訂閱者,和訂閱者相應block
2.當傳送訊號的,遍歷訂閱者,呼叫訂閱者的nextBolck

注:如果訂閱訊號,必須在傳送訊號之前訂閱訊號,不然收不到訊號,有利用區別RACReplaySubject
*/

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    RacSubjectController *racsub = [[RacSubjectController alloc] init];
    racsub.subject = [RACSubject subject];
    [racsub.subject subscribeNext:^(id x) {
        NSLog(@"被通知了%@",x);
    }];
    [self.navigationController pushViewController:racsub animated:YES];
}
複製程式碼

在RacSubjectController.h裡面宣告屬性

@property (nonatomic, strong) RACSubject *subject;
複製程式碼

.m裡面進行資料的傳遞

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
	if (self.subject) {
	    [self.subject sendNext:@1];
	}
}
複製程式碼

5.廣播

//傳送通知
- (void)demo5{
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    //傳送廣播通知
    [center postNotificationName:@"婦女之友" object:nil userInfo:@{@"技巧":@"用心聽"}];
}

//接收通知
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
//RAC的通知不需要我們手動移除
//註冊廣播通知
RACSignal *siganl = [center rac_addObserverForName:@"婦女之友" object:nil];
//設定接收通知的回撥處理
[siganl subscribeNext:^(NSNotification *x) {
    NSLog(@"技巧: %@",x.userInfo[@"技巧"]);
}];
複製程式碼

6.兩個訊號串聯,兩個管串聯,一個管處理完自己的東西,下一個管才開始處理自己的東西

- (void)demo6{
    //建立一個訊號管A
    RACSignal *siganlA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"吃飯"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //建立一個訊號管B
    RACSignal *siganlB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"吃的飽飽的,才可以睡覺的"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //串聯管A和管B
    RACSignal *concatSiganl = [siganlA concat:siganlB];
    //串聯後的接收端處理 ,兩個事件,走兩次,第一個列印siggnalA的結果,第二次列印siganlB的結果
    [concatSiganl subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}
複製程式碼

7.並聯,只要有一個管有東西,就可以列印

- (void)demo7{
  //建立訊號A
    RACSignal *siganlA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"紙廠汙水"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //建立訊號B
    RACSignal *siganlB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"電鍍廠汙水"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //並聯兩個訊號,根上面一樣,分兩次列印
    RACSignal *mergeSiganl = [RACSignal merge:@[siganlA,siganlB]];
    [mergeSiganl subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}
複製程式碼

8.組合,只有兩個訊號都有值,才可以組合

- (void)demo8{
    //定義2個自定義訊號
    RACSubject *letters = [RACSubject subject];
    RACSubject *numbers = [RACSubject subject];
   
    //組合訊號
    [[RACSignal combineLatest:@[letters,numbers] reduce:^(NSString *letter, NSString *number){
        return [letter stringByAppendingString:number];
    }] subscribeNext:^(id x) {
        NSLog(@"%@",x);
       
    }];
   
    //自己控制發生訊號值
    [letters sendNext:@"A"];
    [letters sendNext:@"B"];
    [numbers sendNext:@"1"]; //列印B1
    [letters sendNext:@"C"];//列印C1
    [numbers sendNext:@"2"];//列印C2
}
複製程式碼

9.合流壓縮

- (void)demo9{
    //建立訊號A
    RACSignal *siganlA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"紅"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //建立訊號B
    RACSignal *siganlB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"白"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //合流後處理的是壓縮包,需要解壓後才能取到裡面的值
    [[siganlA zipWith:siganlB] subscribeNext:^(id x) {
        //解壓縮
        RACTupleUnpack(NSString *stringA, NSString *stringB) = x;
        NSLog(@"%@ %@",stringA, stringB);
    }];
}
複製程式碼

10.對映,我可以點石成金

- (void)demo10{
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:nil];
        [subscriber sendCompleted];
        return nil;
    }];
    //對訊號進行改造,改"石"成"金"
    siganl = [siganl map:^id(NSString *value) {
        if ([value isEqualToString:@"石"]) {
            return @"金";
        }
        return value;
    }];
   
    //列印,不論訊號傳送的是什麼,這一步都會走的
    [siganl subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}
複製程式碼

11.過濾,未滿18歲,禁止入內

- (void)demo11{
    RACSignal *singal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@(15)];
        [subscriber sendNext:@(17)];
        [subscriber sendNext:@(21)];
        [subscriber sendNext:@(14)];
        [subscriber sendNext:@(30)];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //過濾訊號,列印
    [[singal filter:^BOOL(NSNumber *value) {
        //大於18歲的,才可以通過
        return value.integerValue >= 18;//return為yes可以通過
    }] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}
複製程式碼

12.秩序(flattenMap方法也可以換成then方法,效果一樣)

-(void)demo12{
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"打蛋液");
        [subscriber sendNext:@"蛋液"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //對訊號進行秩序秩序的第一步
    siganl = [siganl flattenMap:^RACStream *(NSString *value) {
        //處理上一步的RACSiganl的訊號value.這裡的value=@"蛋液"
        NSLog(@"把%@倒進鍋裡面煎",value);
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"煎蛋"];
            [subscriber sendCompleted];
            return nil;
        }];
    }];
    //對訊號進行第二步處理
    siganl = [siganl flattenMap:^RACStream *(id value) {
        NSLog(@"把%@裝載盤裡",value);
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"上菜"];
            [subscriber sendCompleted];
            return nil;
        }];
    }];
   
    //最後列印 最後帶有===上菜
    [siganl subscribeNext:^(id x) {
        NSLog(@"====%@",x);
    }];
}
複製程式碼

13.命令

-(void)demo13{
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        //列印:今天我投降了
      //命令執行代理
        NSLog(@"%@我投降了",input);
        //返回一個RACSignal訊號
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            return nil;
        }];
    }];
    //執行命令
    [command execute:@"今天"];
}
複製程式碼

14.延遲

- (void)demo14{
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"等等我,我還有10s就到了");
        [subscriber sendNext:@"北極"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //延遲10s接受next的玻璃球
    [[siganl delay:10] subscribeNext:^(id x) {
        NSLog(@"我到了%@",x);
    }];
}
複製程式碼

15.重放

- (void)demo15{
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"電影");
        [subscriber sendNext:@"電影"];
        [subscriber sendCompleted];
        return nil;
    }];
   
    //建立該普通訊號的重複訊號
    RACSignal *replaySiganl = [siganl replay];
    //重複接受訊號
    [replaySiganl subscribeNext:^(NSString *x) {
        NSLog(@"小米%@",x);
    }];
    [replaySiganl subscribeNext:^(NSString *x) {
        NSLog(@"小紅%@",x);
    }];
}
複製程式碼

16.定時---每隔8小時服用一次藥

- (void)demo16{
    //建立定時器訊號.定時8小時
    RACSignal *siganl = [RACSignal interval:60 * 60 * 8 onScheduler:[RACScheduler mainThreadScheduler]];
    //定時器執行程式碼
    [siganl subscribeNext:^(id x) {
        NSLog(@"吃藥");
    }];
}
複製程式碼

17.超時

- (void)demo17{
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"我快到了");
        RACSignal *sendSiganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:nil];
            [subscriber sendCompleted];
            return nil;
        }];
       
        //發生訊號要1個小時10分鐘才到
        [[sendSiganl delay:60 * 70] subscribeNext:^(id x) {
          //這裡才傳送next玻璃球到siganl
            [subscriber sendNext:@"我到了"];
            [subscriber sendCompleted];
        }];
        return nil;
    }];
   
    [[siganl timeout:60 * 60 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
        NSLog(@"等了你一個小時,你一直沒來,我走了");
    }];
}
複製程式碼

18.重試

- (void)demo18{
    __block int failedCount = 0;
    //建立訊號
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        if (failedCount < 100) {
            failedCount ++;
            NSLog(@"我失敗了");
            [subscriber sendError:nil];
        }else{
            NSLog(@"經歷了數百次後,我成功了");
            [subscriber sendNext:nil];
        }
        return nil;
    }];
   
    //重試
    RACSignal *retrySiganl = [siganl retry];
    //直到發生next的玻璃球
    [retrySiganl subscribeNext:^(id x) {
        NSLog(@"重要成功了");
    }];
}
複製程式碼

19.節流,不好意思,這裡每一秒只能通過一個人,如果1s內發生多個,只通過最後一個

- (void)demo19{
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      //即使傳送一個next的玻璃球
        [subscriber sendNext:@"A"];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"B"];
        });
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"C"];
            [subscriber sendNext:@"D"];
            [subscriber sendNext:@"E"];
        });
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"F"];
        });
        return nil;
    }];
   
    //對訊號進行節流,限制時間內一次只能通過一個玻璃球
    [[signal throttle:1] subscribeNext:^(id x) {
        NSLog(@"%@通過了",x);
    }];
    /*
    [2015-08-16 22:08:45.677]旅客A
    [2015-08-16 22:08:46.737]旅客B
    [2015-08-16 22:08:47.822]旅客E
    [2015-08-16 22:08:48.920]旅客F
    */
}
複製程式碼

20.條件(takeUntil方法,當給定的signal完成前一直取值)

- (void)demo20{
    RACSignal *takeSiganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      //建立一個定時器訊號,每一秒觸發一次
        RACSignal *siganl = [RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]];
        [siganl subscribeNext:^(id x) {
          //在這裡定時傳送next玻璃球
            [subscriber sendNext:@"直到世界盡頭"];
        }];
        return nil;
    }];

    //建立條件訊號
    RACSignal *conditionSiganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      //設定5s後發生complete玻璃球
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"世界的今天到了,請下車");
            [subscriber sendCompleted];
        });
        return nil;
    }];
   
    //設定條件,takeSiganl訊號在conditionSignal訊號接收完成前,不斷取值
    [[takeSiganl takeUntil:conditionSiganl] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}
複製程式碼

21.RACReplaySubject使用

/**
*  RACReplaySubject建立方法
        1.建立RACSubject
        2.訂閱訊號
        3.傳送訊號
    工作流程:
    1.訂閱訊號,內部儲存了訂閱者,和訂閱者相應的block
    2.當傳送訊號的,遍歷訂閱者,呼叫訂閱者的nextBlock
    3.傳送的訊號會儲存起來,當訂閱者訂閱訊號的時候,會將之前儲存的訊號,一個個作用於新的訂閱者,儲存訊號的容量由capacity決定,這也是有別於RACSubject的
*/

-(void)RACReplaySubject{
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
    [replaySubject subscribeNext:^(id x) {
        NSLog(@" 1 %@",x);
    }];
   
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"2 %@",x);
    }];
    [replaySubject sendNext:@7];
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"3 %@",x);
    }];
}
複製程式碼

22.rac_liftSelector:withSignals使用

//這裡的rac_liftSelector:withSignals 就是幹這件事的,它的意思是當signalA和signalB都至少sendNext過一次,接下來只要其中任意一個signal有了新的內容,doA:withB這個方法就會自動被觸發
-(void)test{
    RACSignal *sigalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        double delayInSeconds = 2.0;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds *NSEC_PER_SEC));
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(popTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"A"];
        });
        return nil;
    }];
   
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"B"];
        [subscriber sendNext:@"Another B"];
        [subscriber sendCompleted];
        return nil;
    }];
    [self rac_liftSelector:@selector(doA:withB:) withSignals:sigalA,signalB, nil];
}
複製程式碼

來源:簡書作者未魏雨辰 《iOS RAC的使用總結》

相關文章