1. 程式人生 > >RAC(ReactiveCocoa)使用方法(一)

RAC(ReactiveCocoa)使用方法(一)

按順序 包裝 多個 sign 設計思想 www 場景 運行 href

RAC(ReactiveCocoa)使用方法(一)
RAC(ReactiveCocoa)使用方法(二)

什麽是RAC?

最近回顧了一下ReactiveCocoa的方法,也看了一些人的文章,現寫篇文章總結一下。
現在這個庫最新支持Swift,如果你要是用Cocoapods的話不指定版本它默認是下載Swift版本,如果依舊想用OC版本就指定一個版本,最好是V2.5版本及以下,否則可能會出現錯誤。最近我試的是V2.5,可以正常使用。
項目中用Cocoapods使用:pod "ReactiveCocoa", ‘~> 2.5‘
那什麽是RAC勒?想必大家隨便谷歌一下就一大片這個概念和文章。
RAC具有函數響應式編程特性,由Matt Diephouse

開源的一個應用於iOS和OS X的新框架。

部分參考來源

為什麽要使用RAC?

因為RAC具有高聚合低耦合的思想,使用RAC會讓代碼更簡潔,邏輯更清晰。再結合MVVM架構,讓你瞬間爽爆了!

RAC有很多的類,為很多的UI控件都拓展了方法,使得開發大大的簡便化,這裏就簡單的介紹開發過程中用到的方法。
打開應用的初始ViewController,引入ReactiveCocoa的頭文件。

#import <ReactiveCocoa/ReactiveCocoa.h> 

在控制器中創建一個TextField,SB拖入更方便,然後如下

[self.TextField.rac_textSignal subscribeNext:^(id x){
  NSLog(@"x:%@", x);
}];

編譯運行,在輸入框中輸入文字。註意打印信息的輸出應該和下面的類似。

2017-11-29 10:26:25.152197+0800 MVVM-Demo[1089:230607] x:a
2017-11-29 10:26:25.159596+0800 MVVM-Demo[1089:230607] x:ah
2017-11-29 10:26:25.385413+0800 MVVM-Demo[1089:230607] x:ahv
2017-11-29 10:26:25.576558+0800 MVVM-Demo[1089:230607] x:ahva
2017-11-29 10:26:25.764013+0800 MVVM-Demo[1089:230607] x:ahvah
2017-11-29 10:26:25.784379+0800 MVVM-Demo[1089:230607] x:ahvahv
2017-11-29 10:26:25.853596+0800 MVVM-Demo[1089:230607] x:ahvahvj
2017-11-29 10:26:25.868552+0800 MVVM-Demo[1089:230607] x:ahvahvja
2017-11-29 10:26:26.002545+0800 MVVM-Demo[1089:230607] x:ahvahvjav
2017-11-29 10:26:26.062553+0800 MVVM-Demo[1089:230607] x:ahvahvjavj

當你看到這些打印信息,你是不是覺得很神奇,都沒有監聽TextField的方法,它咋就那麽牛逼勒。其實RAC內部就幫你做了許多事情。你只要調用相應控件的RAC方法就可以監聽到它們的狀態了。

那麽它是怎麽監聽怎麽做到的勒?
這裏要講幾個很重要的RAC類,不涉及RAC原理,內部怎麽實現還要大家去閱讀源碼了。

  • RACSiganl

    1、RACSiganl信號類,表示將來有數據傳遞,有數據改變,信號內部接收到數據,就會馬上發出數據,外部就可以接收到數據了。就像剛剛上面的例子一樣。
    2、默認信號都是冷信號,就是這個值改變了它不會觸發,只有訂閱(調用信號RACSignal的subscribeNext訂閱)了這個信號,這個信號才會變為熱信號(值一改變就觸發),才會觸發。

    RACSiganl簡單使用:

    ```
    // 1.創建信號
    RACSignal siganl = [RACSignal createSignal:^RACDisposable (id

    // 每當有訂閱者訂閱信號,就會調用block。
    // 2.發送信號
    [subscriber sendNext:@"我是一個信號??"];
    
    // 如果不在發送數據,最好發送信號完成,內部會自動調用[RACDisposable disposable]取消訂閱信號。
    [subscriber sendCompleted];
    
    return [RACDisposable disposableWithBlock:^{
        // 當信號發送完成或者發送錯誤,就會自動執行這個block,取消訂閱信號。
        // 執行完Block後,當前信號就不在被訂閱了。
        NSLog(@"dealloc");
    
    }];

    }];

    // 3.訂閱信號,才會激活信號.
    [siganl subscribeNext:^(id x) {
    // block調用時刻:每當有信號發出數據,就會調用block.
    NSLog(@"數據: %@",x);
    }];


打印如下
 ```
2017-11-29 11:04:34.383754+0800 MVVM-Demo[1185:379135] 數據:我是一個信號??
2017-11-29 11:04:34.383878+0800 MVVM-Demo[1185:379135] dealloc
  • RACSubscriber

    RACSubscriber訂閱者,用於發送信號,這是一個協議,不是一個類,只要遵守這個協議,並且實現方法才能成為訂閱者。通過create創建的信號,都有一個訂閱者,幫助他發送數據。

  • RACDisposable

    RACDisposable用於取消訂閱或者清理資源,當信號發送完成或者發送錯誤的時候,就會自動觸發它。 當你不想監聽某個信號時,可以通過它主動取消訂閱信號。

  • RACSubject
    RACSubject信號提供者,自己可以充當信號,又能發送信號。通常用來代替代理,有了它,就不必要定義代理了。
  • RACReplaySubject

    RACReplaySubject重復提供信號類,RACSubject的子類。

  • RACReplaySubjectRACSubject區別:
    RACReplaySubject可以先發送信號,在訂閱信號,RACSubject就不可以。
    ```
  • 如果一個信號每被訂閱一次,就需要把之前的值重復發送一遍,使用重復提供信號類。
  • 可以設置capacity數量來限制緩存的value的數量, 即只緩存最新的幾個值。
    ```

RACReplaySubject使用步驟:
  • 創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。
  • 可以先訂閱信號,也可以先發送信號。
  • 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
  • 發送信號 sendNext:(id)value
RACReplaySubject:底層實現和RACSubject不一樣。
  • 調用sendNext發送信號,把值保存起來,然後遍歷剛剛保存的所有訂閱者,一個一個調用訂閱者的nextBlock。
  • 調用subscribeNext訂閱信號,遍歷保存的所有值,一個一個調用訂閱者的nextBlock
  • 如果想當一個信號被訂閱,就重復播放之前所有值,需要先發送信號,在訂閱信號。
  • 先保存值,在訂閱值。
    // 1.創建信號
    RACReplaySubject *replaySubject = [RACReplaySubject subject];

    // 2.發送信號
    [replaySubject sendNext:@1];
    [replaySubject sendNext:@2];

    // 3.訂閱信號
    [replaySubject subscribeNext:^(id x) {

        NSLog(@"第一個訂閱者接收到的數據%@",x);
    }];

    // 訂閱信號
    [replaySubject subscribeNext:^(id x) {

        NSLog(@"第二個訂閱者接收到的數據%@",x);
    }];

打印如下:

2017-11-29 11:02:07.468379+0800 MVVM-Demo[1158:370610] 第一個訂閱者接收到的數據1
2017-11-29 11:02:07.468477+0800 MVVM-Demo[1158:370610] 第一個訂閱者接收到的數據2
2017-11-29 11:02:07.468592+0800 MVVM-Demo[1158:370610] 第二個訂閱者接收到的數據1
2017-11-29 11:02:07.468722+0800 MVVM-Demo[1158:370610] 第二個訂閱者接收到的數據2
RACSubject使用步驟
  • 創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。
  • 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
  • 發送信號 sendNext:(id)value
RACSubject:底層實現和RACSignal不一樣。
  • 調用subscribeNext訂閱信號,只是把訂閱者保存起來,並且訂閱者的nextBlock已經賦值了。
  • 調用sendNext發送信號,遍歷剛剛保存的所有訂閱者,一個一個調用訂閱者的nextBlock。
    ```
    // 1.創建信號
    RACSubject *subject = [RACSubject subject];
    // 2.訂閱信號
    [subject subscribeNext:^(id x) {
    // 當信號發出新值,就會調用.
    NSLog(@"第一個訂閱者%@",x);
    }];
    [subject subscribeNext:^(id x) {
    // 當信號發出新值,就會調用.
    NSLog(@"第二個訂閱者%@",x);
    }];

// 3.發送信號
[subject sendNext:@"我是一個信號??"];

* #####RACTuple
 `RACTuple `元組類, 類似NSArray,用來包裝值.

* #####RACSequence
`RACSequence `集合類,用於代替NSArray, NSDictionary,可以使用它來快速遍歷數組和字典。

* `RACSequence `和`RACTuple `簡單使用

`NSArray`
NSArray *arr = @[@"哈哈",@"呵呵", @"嘿嘿", @"哼哼"];
[arr.rac_sequence.signal subscribeNext:^(id x) {
    NSLog(@"x: %@", x);
}];

打印如下
2017-11-29 11:19:27.081935+0800 MVVM-Demo[1267:428560] x: 哈哈
2017-11-29 11:19:27.082227+0800 MVVM-Demo[1267:428560] x: 呵呵
2017-11-29 11:19:27.082350+0800 MVVM-Demo[1267:428560] x: 嘿嘿
2017-11-29 11:19:27.082664+0800 MVVM-Demo[1267:428560] x: 哼哼

`原理:`
* 通過`arr.rac_sequence`把數據arr轉化成集合RACSequence
* 通過`arr.rac_sequence.signal `把集合RACSequence轉化成了信號
*通過subscribeNext訂閱信號,把遍歷集合

`NSDictionary `
// 2.遍歷字典,遍歷出來的鍵值對會包裝成RACTuple(元組對象)
 NSDictionary *dict = @{@"name": @"soliloquy", @"age": @26};
[dict.rac_sequence.signal subscribeNext:^(id x) {
    // 解包元組,會把元組的值,按順序給參數裏面的變量賦值
    RACTupleUnpack(NSString *key,NSString *value) = x;
    NSLog(@"%@ %@",key,value);
}];

打印如下
2017-11-29 11:51:08.027070+0800 MVVM-Demo[1367:471752] name soliloquy
2017-11-29 11:51:08.027526+0800 MVVM-Demo[1367:471752] age 26


`字典轉模型`

NSArray arr = @[
@{@"name": @"soliloquy", @"age": @26},
@{@"name": @"ptl", @"age": @21},
];
[arr.rac_sequence.signal subscribeNext:^(id x) {
// 運用RAC遍歷字典,x:字典
Model
item = [Model modelWithDict:x];
[array addObject:item];

}];

`其他用法`
NSArray *arr = @[
                 @{@"name": @"soliloquy", @"age": @26},
                 @{@"name": @"ptl", @"age": @21},
                 ];

NSArray *ay = [[arr.rac_sequence map:^id(id value) {
    
    return [Persion modelWithDict: value];
    
}] array];

NSLog(@"ay: %@", ay);

for (Persion *model in ay) {
    NSLog(@"%@---%zd", model.name, model.age);
}
打印如下

2017-11-29 12:18:24.024939+0800 MVVM-Demo[1631:553078] ay: (
"


* #####RACCommand

`RACCommand `RAC 中用於處理事件的類,可以把事件如何處理,事件中的數據如何傳遞,包裝到這個類中,他可以很方便的監控事件的執行過程。
比如:監聽按鈕點擊,網絡請求

`RACCommand簡單使用`

一、RACCommand使用步驟:
1.創建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
2.在signalBlock中,創建RACSignal,並且作為signalBlock的返回值
3.執行命令 - (RACSignal *)execute:(id)input

二、RACCommand使用註意:
1.signalBlock必須要返回一個信號,不能傳nil.
2.如果不想要傳遞信號,直接創建空的信號[RACSignal empty];
3.RACCommand中信號如果數據傳遞完,必須調用[subscriber sendCompleted],這時命令才會執行完畢,否則永遠處於執行中。
4.RACCommand需要被強引用,否則接收不到RACCommand中的信號,因此RACCommand中的信號是延遲發送的。

三、RACCommand設計思想
1.在RAC開發中,通常會把網絡請求封裝到RACCommand,直接執行某個RACCommand就能發送請求。
2.當RACCommand內部請求到數據的時候,需要把請求的數據傳遞給外界,這時候就需要通過signalBlock返回的信號傳遞了。

四、如何拿到RACCommand中返回信號發出的數據。
1.RACCommand有個執行信號源executionSignals,這個是signal of signals(信號的信號),意思是信號發出的數據是信號,不是普通的類型。
2.訂閱executionSignals就能拿到RACCommand中返回的信號,然後訂閱signalBlock返回的信號,就能獲取發出的值。

五、監聽當前命令是否正在執行executing
六、使用場景, 監聽按鈕點擊,網絡請求

// 1.創建命令 強引用命令,不要被銷毀,否則接收不到數據

self.command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
// 創建空信號,必須返回信號
// return [RACSignal empty];

    // 2.創建信號,用來傳遞數據
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        NSArray *arr = @[@"123",@"321", @"132", @"312"];
        [subscriber sendNext:arr];
        // 註意:數據傳遞完,最好調用sendCompleted,這時命令才執行完畢。
        [subscriber sendCompleted];
        return nil;
    }];
    
}];

// 3.訂閱RACCommand中的信號
[command.executionSignals subscribeNext:^(id x) {

    [x subscribeNext:^(id x) {

        NSLog(@"數據為: %@",x);
    }];

}];

```

打印如下

2017-11-29 12:41:50.364091+0800 MVVM-Demo[1844:622694] 數據為: (
    123,
    321,
    132,
    312
)

RAC高級用法

 // switchToLatest:用於signal of signals,獲取signal of signals發出的最新信號,也就是可以直接拿到RACCommand中的信號
    [self.command.executionSignals.switchToLatest subscribeNext:^(id x) {
        
        NSLog(@"x: %@",x);
    }];
    
    // 4.監聽命令是否執行完畢,默認會來一次,可以直接跳過,skip表示跳過第一次信號。
    [[command.executing skip:1] subscribeNext:^(id x) {
        
        if ([x boolValue] == YES) {
            NSLog(@"正在執行");
            
        }else{
            NSLog(@"執行完成");
        }
        
    }];
   // 5.執行命令
    [self.conmmand execute:nil];
  • RACMulticastConnection
    RACMulticastConnection用於當一個信號被多個訂閱時,為了保證創建信號時避免多次調用創建信號中的block造成多次發生數據,可以使用這個該類處理。
    RACMulticastConnection通過RACSignal的-publish或者-muticast:方法創建.

######RACMulticastConnection使用步驟:

  • 創建信號
    + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
  • 創建連接
    RACMulticastConnection *connect = [signal publish];
  • 訂閱信號,註意:訂閱的不在是之前的信號,而是連接的信號。 [connect.signal subscribeNext:nextBlock];
  • 連接
    [connect connect];

    // 1.創建請求信號
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    
    
        NSLog(@"發送數據");
    
        return nil;
    }];
    // 2.訂閱信號
    [signal1 subscribeNext:^(id x) {
    
        NSLog(@"接收數據");
    
    }];
    // 2.訂閱信號
    [signal1 subscribeNext:^(id x) {
    
        NSLog(@"接收數據");
    
    }];
    
    // 3.運行結果,會執行兩遍發送請求,也就是每次訂閱都會發送一次請求
    
    
    // RACMulticastConnection:解決重復請求問題
    // 1.創建信號
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    
    
        NSLog(@"發送請求");
        [subscriber sendNext:@"我是數據源"];
    
        return nil;
    }];
    
    // 2.創建連接
    RACMulticastConnection *connect = [signal publish];
    
    // 3.訂閱信號,
    // 註意:訂閱信號,也不能激活信號,只是保存訂閱者到數組,必須通過連接,當調用連接,就會一次性調用所有訂閱者的sendNext:
    [connect.signal subscribeNext:^(id x) {
    
        NSLog(@"訂閱者一:%@", x);
    
    }];
    
    [connect.signal subscribeNext:^(id x) {
    
        NSLog(@"訂閱者二:%@", x);
    
    }];
    
    // 4.連接,激活信號
    [connect connect];

    打印如下
    ```
    2017-11-29 13:13:40.735831+0800 MVVM-Demo[2088:721509] 發送數據
    2017-11-29 13:13:40.736024+0800 MVVM-Demo[2088:721509] 發送數據
    2017-11-29 13:13:40.736550+0800 MVVM-Demo[2088:721509] 發送請求
    2017-11-29 13:13:40.736688+0800 MVVM-Demo[2088:721509] 訂閱者一:我是數據源
    2017-11-29 13:13:40.736777+0800 MVVM-Demo[2088:721509] 訂閱者二:我是數據源

``可以看出RACSignal 被調了兩次,每次訂閱一次都會發送請求,這樣就會導致多次請求,而使用RACMulticastConnection `就只有在創建信號時調了一次。

RACMulticastConnection底層原理:
  • 創建connect
    connect.sourceSignal -> RACSignal(原始信號) connect.signal -> RACSubject
  • 訂閱connect.signal,會調用RACSubject的subscribeNext,創建訂閱者,而且把訂閱者保存起來,不會執行block。
  • [connect connect]內部會訂閱RACSignal(原始信號),並且訂閱者是RACSubject
    訂閱原始信號,就會調用原始信號中的didSubscribe
    didSubscribe,拿到訂閱者調用sendNext,其實是調用RACSubject的sendNext
    *RACSubjectsendNext,會遍歷RACSubject所有訂閱者發送信號。
    拿到第二步所有的訂閱者,調用他們的nextBlock

RAC(ReactiveCocoa)使用方法(一)