ReactiveCocoa(OC版)

OneAlon發表於2017-12-20

一.Getting started專案中使用ReactiveCocoa

在這裡筆者使用Cocoapods安裝ReactiveCocoa,在專案中建立podfile檔案,使用的是2.5版本.

platform :ios, '8.0'
#use_frameworks!

target '你的專案名稱’ do
  pod 'ReactiveCocoa', '~> 2.5’
end
複製程式碼

執行安裝命令

pod install --no-repo-update
複製程式碼

二.ReactiveCocoa常見類

1.RACSignal訊號類 RACSignal訊號類表示當資料改變時,在訊號內部會利用訂閱者傳送資料.RACSignal預設是一個冷訊號,只有被訂閱以後才會變成熱訊號. RACSignal訊號類的簡單使用:

    // 1.建立訊號
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 3.利用訂閱者傳送資料
        // 只有當有訂閱者訂閱時,才會呼叫這個block
        [subscriber sendNext:@"這是傳送的資料"];
        return nil;
    }];
    
    // 2.訂閱訊號
    [signal subscribeNext:^(id x) {
       NSLog(@"接收到資料:%@",x);
    }];
複製程式碼

2.RACSubscriber訂閱者 RACSubscriber是一個協議,任何遵循RACSubscriber協議的物件並且實現協議方法都可以是一個訂閱者,訂閱者可以幫助訊號傳送資料. RACSubscriber協議中有四個方法.

- (void)sendNext:(id)value;
- (void)sendError:(NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
複製程式碼

3.RACDisposable RACDisposable用於取消訂閱和清理資源,當訊號傳送完成或傳送錯誤時會自動呼叫.

    // 1.建立訊號
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 3.利用訂閱者傳送資料
        [subscriber sendNext:@"這是傳送的資料"];
        // 如果為未呼叫,當訊號傳送完成或傳送錯誤時會自動呼叫
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"資源被清理了");
        }];
    }];
    
    // 2.訂閱訊號
    [signal subscribeNext:^(id x) {
       NSLog(@"接收到資料:%@",x);
    }];
複製程式碼

4.RACSubject訊號提供者 RACSubject繼承RACSignal,又遵循了RACSubscriber協議,所以既可以充當訊號,又可以傳送訊號,通常用它代替代理.

    // 1.建立訊號
    RACSubject *subject = [RACSubject subject];
    
    // 2.訂閱訊號
    [subject subscribeNext:^(id x) {
       NSLog(@"接收到資料:%@",x);
    }];
    
    // 3.傳送訊號
    [subject sendNext:@"傳送資料"];
複製程式碼

RACSubject的底層實現

  • 在執行[RACSubject subject]時,RACSubject會在初始化時建立disposable物件屬性和subscribers訂閱者陣列.

image.png

  • 在執行subscribeNext訂閱訊號時,會建立一個訂閱者RACSubscriber,並將訂閱者RACSubscriber新增到subscribers訂閱者陣列.

image.png

  • 在執行sendNext傳送訊號時,會遍歷subscribers訂閱者陣列,執行sendNext

image.png

5.RACReplaySubject RACReplaySubject重複提供訊號類,RACSubject的子類.由於RACReplaySubject的底層實現和RACSubject不同,RACReplaySubject可以先傳送資料,再訂閱訊號.

    // 1.建立訊號
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
    
    // 2.訂閱訊號
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"訂閱訊號,%@",x);
    }];
    
    // 3.傳送資料
    [replaySubject sendNext:@"傳送的資料"];
複製程式碼

RACReplaySubject的底層實現

  • 在執行[RACReplaySubject subject]時,建立一個valuesReceived陣列

image.png

  • 在執行subscribeNext時,建立訂閱者,遍歷valuesReceived陣列,利用訂閱者執行sendNext傳送valuesReceived中的資料.

image.png

  • 在執行sendNext時,將要傳送的資料儲存到valuesReceived陣列中,執行sendNext

image.png

6.RACMulticastConnection 我們在使用RACsignal,RACReplaySubject或者RACReplaySubject時,當一個訊號被多個訂閱者訂閱時,在訊號內部的block或被呼叫多次,有時這樣並不能滿足我們的需求,我們想要訊號被多個訂閱者訂閱時,訊號內部的block只被執行一次,那麼RACMulticastConnection就能幫助我們完成需求.

    // 1.建立訊號
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 3.傳送資料
        NSLog(@"傳送資料");
        [subscriber sendNext:@"傳送資料"];
        return nil;
    }];
    
    RACMulticastConnection *connection = [signal publish];
    
    // 2.訂閱訊號
    [connection.signal subscribeNext:^(id x) {
        NSLog(@"接收到資料1:%@",x);
    }];
    [connection.signal subscribeNext:^(id x) {
        NSLog(@"接收到資料2:%@",x);
    }];
    
    // 4.啟用訊號
    [connection connect];
複製程式碼

log中的列印如下

2017-06-20 16:55:50.809 MVVMRACDemoOC[2848:856666] 傳送資料
2017-06-20 16:55:50.810 MVVMRACDemoOC[2848:856666] 接收到資料1:傳送資料
2017-06-20 16:55:50.810 MVVMRACDemoOC[2848:856666] 接收到資料2:傳送資料
複製程式碼

7.RACCommand RACCommand是處理事件的類,可以把事件如何處理,事件中的資料如何傳遞,包裝到這個類中. 使用一個demo說明RACCommand:監聽按鈕的點選,傳送網路請求.

    // 1.建立命令
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        NSLog(@"接收到命令:%@", input);
        // 返回一個訊號,不能為空.(訊號中的訊號)
        // 3.建立訊號用來傳遞資料
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"訊號中的訊號傳送的資料"];
            // 注意:資料傳遞完成,要呼叫sendCompleted才能執行完畢
            [subscriber sendCompleted];
            return nil;
        }];
    }];
    self.command = command;
    
    // 2.訂閱訊號中的訊號(必須要在執行命令前訂閱)
    [command.executionSignals.switchToLatest subscribeNext:^(id x) {
        NSLog(@"接收到訊號中的訊號傳送的資料:%@",x);
    }];
    
    
    // 4.執行命令
    [command execute:@1];
    
    [[command.executing skip:1] subscribeNext:^(id x) {
        if ([x boolValue] == YES) {
            NSLog(@"正在執行");
        }else{
            NSLog(@"未開始/執行完成");
        }
    }];
複製程式碼

log中的列印

2017-06-20 17:26:28.013 MVVMRACDemoOC[3166:970238] 接收到命令:1
2017-06-20 17:26:28.016 MVVMRACDemoOC[3166:970238] 正在執行
2017-06-20 17:26:28.016 MVVMRACDemoOC[3166:970238] 接收到訊號中的訊號傳送的資料:訊號中的訊號傳送的資料
2017-06-20 17:26:28.017 MVVMRACDemoOC[3166:970238] 未開始/執行完成
複製程式碼

三.常見巨集

  1. RAC(TARGET, [KEYPATH, [NIL_VALUE]])給某個物件的某個屬性做繫結.
// 只要passwordTextField內容變化,accountTextField的text就會跟著改變
RAC(self.accountTextField, text) = self.passwordTextField.rac_textSignal;
複製程式碼
  1. RACObserve(self, name)監聽某個物件的某個屬性,返回訊號
    // 監聽passwordTextField背景色的改變
    [RACObserve(self.passwordTextField, backgroundColor) subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
複製程式碼
  1. @weakify(Obj)@strongify(Obj)一般用來防止迴圈引用,組合使用.
  2. RACTuplePack把資料包裝成RACTuple(元組類)
    // 把引數中的資料包裝成元組
    RACTuple *tuple = RACTuplePack(@1,@2);
複製程式碼
  1. RACTupleUnpack把RACTuple(元組類)解包成對應的資料
    // 把引數中的資料包裝成元組
    RACTuple *tuple = RACTuplePack(@"OneAlon",@"HangZhou");

    // 解包元組,會把元組的值,按順序給引數裡面的變數賦值
    RACTupleUnpack(NSString *name,NSNumber *address) = tuple;
複製程式碼

四.常見用法

  1. 代替代理 代替代理有兩種方法,一種是使用RACSubject代替代理,另一種是使用rac_signalForSelector方法代替代理. 這裡模擬一個需求:自定義一個紅色的view,在view中有一個按鈕,監聽按鈕的點選. 如果不使用RAC,在紅色的view中定義一個代理屬性,點選按鈕的時候通知代理做事情. 如果使用RAC,直接讓紅色的view呼叫rac_signalForSelector方法即可.
    [[self.redView rac_signalForSelector:@selector(buttonClick:)] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
複製程式碼

2.代替KVO 監聽紅色view的背景色的改變

    [[self.redView rac_valuesAndChangesForKeyPath:@"backgroundColor" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
複製程式碼

3.監聽事件

    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        NSLog(@"按鈕被點選了");
    }];
複製程式碼

4.代替通知

    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
        NSLog(@"鍵盤彈出");
    }];
複製程式碼

5.監聽文字框文字改變

   [_textField.rac_textSignal subscribeNext:^(id x) {

       NSLog(@"文字改變了%@",x);
   }];
複製程式碼

6.處理當介面有多次請求時,需要都獲取到資料時,才能展示介面,rac_liftSelector:withSignalsFromArray:Signals

五.Demo

做了一個小demo,僅供學習使用.

六.參考文獻

Objc中國 MVVM 介紹 ReactiveCocoa 和 MVVM 入門 最快讓你上手ReactiveCocoa之基礎篇 最快讓你上手ReactiveCocoa之進階篇 學習視訊

相關文章