iOS開發ReactiveCocoa學習筆記(一)


學習 RAC 我們首先要了解 RAC 都有哪些類

  • RACSignal
  • RACSubject
  • RACSequence
  • RACMulticastConnection
  • RACCommand

在學習的時候寫了一個小 demo 來分別介紹每個類的作用,gitHub 地址: https://github.com/SummerHH/ReactiveCocoa.git 

demo 的目錄結構如下

 

RAC學習起來的特點

  • 學習起來比較難
  • 團隊開發的時候需要謹慎使用
  • 團隊代碼需要不斷的評審,保證團隊中所有人代碼的風格一致!避免閱讀代碼的困難

RAC 導入:

使用  pod 'ReactiveCocoa', '~> 2.5' 導入

這里版本說下2.5以下是 Object-C 不支持 swift

2.5 以上開始支持 swift

項目如果是 OC 寫的話建議導入2.5這個版本

RACSignal:

 RACSiganl:信號類,一般表示將來有數據傳遞,只要有數據改變,信號內部接收到數據,就會馬上發出數據。

 信號類(RACSiganl),只是表示當數據改變時,信號內部會發出數據,它本身不具備發送信號的能力,而是交給內部一個訂閱者去發出。

     

 默認一個信號都是冷信號,也就是值改變了,也不會觸發,只有訂閱了這個信號,這個信號才會變為熱信號,值改變了才會觸發。

     

  如何訂閱信號:調用信號RACSignalsubscribeNext就能訂閱。

//1.創建信號
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
       
        //3.發送信號
        [subscriber sendNext:@"發送信號"];
        /**
         如果不在發送數據,最好發送信號完成,內部會自動調用[RACDisposable disposable]取消訂閱信號
         */
        [subscriber sendCompleted];
        
        //取消訂閱方法
        return [RACDisposable disposableWithBlock:^{
            //block調用時刻:當信號發送完成或者發送錯誤,就會自動執行這個block,取消訂閱信號
            // 執行完Block后,當前信號就不在被訂閱了。
            NSLog(@"信號銷毀了");
        }];
    }];
    
    //2.訂閱信號
    [signal subscribeNext:^(id x) {
        NSLog(@"訂閱信號:%@",x);
    }];

   //輸出

    //訂閱信號:發送信號

    //信號銷毀了

 

     RACSignal底層實現

     1.創建信號,首先把didSubscribe保存到信號中,還不會觸發。

     2.當信號被訂閱,也就是調用signalsubscribeNext:nextBlock

     2.1 subscribeNext內部會調用siganldidSubscribe

     2.2 subscribeNext內部會創建訂閱者subscriber,並且把nextBlock保存到subscriber

     3.siganldidSubscribe中調用[subscriber sendNext:@"發送信號"];

     3.1 sendNext底層其實就是執行subscribernextBlock

 

注意:

當一個信號被全局變量保存值的時候,我們要手動取消訂閱

RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
       
        /**
         有一個全局變量保存值就不會走下面取消訂閱方法
         */
        _subscriber = subscriber;
        
        [subscriber sendNext:@"123"];
        [subscriber sendCompleted];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"走銷毀這個方法了");
        }];
    }];

RACDisposable 取消訂閱

    //訂閱信號返回RACDisposable
    RACDisposable *disposable = [signal1 subscribeNext:^(id x) {
        NSLog(@"接收打印的值:%@",x);
    }];
    
    // 默認一個信號發送數據完畢們就會主動取消訂閱.
    // 只要訂閱者在,就不會自動取消信號訂閱
    // 手動取消訂閱者
    [disposable dispose];

RACSubscriber:

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

RACDisposable:用於取消訂閱或者清理資源,當信號發送完成或者發送錯誤的時候,就會自動觸發它。

     使用場景:不想監聽某個信號時,可以通過它主動取消訂閱信號。

     

     RACSubject:RACSubject:信號提供者,自己可以充當信號,又能發送信號。

     使用場景:通常用來代替代理,有了它,就不必要定義代理了。

     

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

     

     RACReplaySubjectRACSubject區別:

     RACReplaySubject可以先發送信號,在訂閱信號,RACSubject就不可以。

     使用場景一:如果一個信號每被訂閱一次,就需要把之前的值重復發送一遍,使用重復提供信號類。

     使用場景二:可以設置capacity數量來限制緩存的value的數量,即只緩充最新的幾個值。

   

    // RACSubject使用步驟

    // 1.創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block

    // 2.訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

    // 3.發送信號 sendNext:(id)value

 //1.創建信號
    RACSubject *subject = [RACSubject subject];
    //2.訂閱信號
    [subject subscribeNext:^(id x) {
       //當信號發出新值,就會調用.
        NSLog(@"第一個訂閱者%@",x);
    }];
    
    [subject subscribeNext:^(id x) {
        NSLog(@"第二個訂閱者%@",x);
    }];
    
    //3.發送信號
    [subject sendNext:@"123456"];
    //4.發送信號完成,內部會自動取消訂閱者
    [subject sendCompleted];
    
    //輸出:
    // 第一個訂閱者123456
    // 第二個訂閱者123456

RACReplaySubject

 

 

 RACReplaySubject使用步驟:

 

  1.創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block

 

 2.可以先訂閱信號,也可以先發送信號。

 

  2.1 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

 

  2.2 發送信號 sendNext:(id)value 

 

   RACReplaySubject:底層實現和RACSubject不一樣。

 

  1.調用sendNext發送信號,把值保存起來,然后遍歷剛剛保存的所有訂閱者,一個一個調用訂閱者的nextBlock

 

   2.調用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);
    }];
    
    //輸出:
    //第一個訂閱者接收到的數據1
    //第一個訂閱者接收到的數據2
    //第二個訂閱者接收到的數據1
    //第二個訂閱者接收到的數據2

RAC集合

RACTuple:元組類,類似NSArray,用來包裝值.

 /**
     RACTuple:元組類,類似NSArray,用來包裝值.
     */
    //元組
    RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"123",@"345",@1]];
    NSString *first = tuple[0];
    NSLog(@"%@",first);
    
    //輸出:
    //123

數組

   NSArray *arr = @[@"213",@"321",@1];
    //RAC 集合
//    RACSequence *sequence = arr.rac_sequence;
//    // 把集合轉換成信號
//    RACSignal *signal = sequence.signal;
//    //訂閱集合信號,內部會自動遍歷所有的元素發出來
//    [signal subscribeNext:^(id x) {
//        NSLog(@"遍歷數組%@",x);
//    }];
    //輸出:
    /**
     遍歷數組213
     遍歷數組321
     遍歷數組1
     */
    
    //高級寫法
    [arr.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"高級寫法遍歷數組打印%@",x);
    }];
    
    //輸出:
    /**
     高級寫法遍歷數組打印213
     高級寫法遍歷數組打印321
     高級寫法遍歷數組打印1
     */

字典

 NSDictionary *dict = @{@"sex":@"",@"name":@"蒼老師",@"age":@18};
    
    //轉換成集合
    [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
//        NSString *key = x[0];
//        NSString *value = x[1];
//        NSLog(@"%@ %@",key,value);
        // RACTupleUnpack:用來解析元組
        // 宏里面的參數,傳需要解析出來的變量名
        //= 右邊,放需要解析的元組
        RACTupleUnpack(NSString *key, NSString *value) = x;
        NSLog(@"%@ %@",key,value);
     
    }];
    //輸出:
    /**
     sex 女
     name 蒼老師
     age 18
     */

字典轉模型

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
    NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
    
    //    NSMutableArray *arr = [NSMutableArray array];
    //    // rac_sequence注意點:調用subscribeNext,並不會馬上執行nextBlock,而是會等一會。
    
    //    [dictArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
    //        Flag *flag = [Flag flagWithDict:x];
    //        [arr addObject:flag];
    //    }];
    
    //高級用法
    // map:映射的意思,目的:把原始值value映射成一個新值
    // array: 把集合轉換成數組
    // 底層實現:當信號被訂閱,會遍歷集合中的原始值,映射成新值,並且保存到新的數組里。
    NSArray *arr = [[dictArr.rac_sequence map:^id(NSDictionary *value) {
        return [Flag flagWithDict:value];
    }] array];
    
    NSLog(@"%@",arr);

 

RACMulticastConnection:

RACMulticastConnection:用於當一個信號,被多次訂閱時,為了保證創建信號時,避免多次調用創建信號中的block,造成副作用,可以使用這個類處理。

 使用注意:RACMulticastConnection通過RACSignal-publish或者-muticast:方法創建.

    RACMulticastConnection簡單使用:

     RACMulticastConnection使用步驟:

     1.創建信號 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

     2.創建連接 RACMulticastConnection *connect = [signal publish];

     3.訂閱信號,注意:訂閱的不在是之前的信號,而是連接的信號。 [connect.signal subscribeNext:nextBlock]

     4.連接 [connect connect]

    

     RACMulticastConnection底層原理:

     1.創建connectconnect.sourceSignal -> RACSignal(原始信號)  connect.signal -> RACSubject

     2.訂閱connect.signal,會調用RACSubjectsubscribeNext,創建訂閱者,而且把訂閱者保存起來,不會執行block

     3.[connect connect]內部會訂閱RACSignal(原始信號),並且訂閱者是RACSubject

     3.1.訂閱原始信號,就會調用原始信號中的didSubscribe

     3.2 didSubscribe,拿到訂閱者調用sendNext,其實是調用RACSubjectsendNext

     4.RACSubjectsendNext,會遍歷RACSubject所有訂閱者發送信號。

     4.1 因為剛剛第二步,都是在訂閱RACSubject,因此會拿到第二步所有的訂閱者,調用他們的nextBlock

    

     需求:假設在一個信號中發送請求,每次訂閱一次都會發送請求,這樣就會導致多次請求。

     解決:使用RACMulticastConnection就能解決.

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

 

RACCommand

RACCommand:RAC中用於處理事件的類,可以把事件如何處理,事件中的數據如何傳遞,包裝到這個類中,他可以很方便的監控事件的執行過程。

     

使用場景:監聽按鈕點擊,網絡請求

 

一、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],這時命令才會執行完畢,否則永遠處於執行中。

三、RACCommand設計思想:內部signalBlock為什么要返回一個信號,這個信號有什么用。

1.RAC開發中,通常會把網絡請求封裝到RACCommand,直接執行某個RACCommand就能發送請求。

2.RACCommand內部請求到數據的時候,需要把請求的數據傳遞給外界,這時候就需要通過signalBlock返回的信號傳遞了。

四、如何拿到RACCommand中返回信號發出的數據。

1.RACCommand有個執行信號源executionSignals,這個是signal of signals(信號的信號),意思是信號發出的數據是信號,不是普通的類型。

2.訂閱executionSignals就能拿到RACCommand中返回的信號,然后訂閱signalBlock返回的信號,就能獲取發出的值。

五、監聽當前命令是否正在執行executing

六、使用場景,監聽按鈕點擊,網絡請求

//1.創建命令
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
       
        NSLog(@"執行命令");
        //創建空信號,必須返回信號
        //return [RACSignal empty];
        
        //2.創建信號,用來傳遞數據
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
           
            [subscriber sendNext:@"請求數據"];
            
            //注意:數據傳遞完,最好調用sendCompleted,這時命令才執行完畢。
            [subscriber sendCompleted];
            
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"數據銷毀");
            }];
        }];
    }];
    
    //強引用命令,不要被銷毀,否則接收不到數據
    _command = command;
    
    //監聽事件有咩有完成
    [command.executing subscribeNext:^(id x) {
        if ([x boolValue] == YES) { // 當前正在執行
            NSLog(@"當前正在執行");
        }else{
            // 執行完成/沒有執行
            NSLog(@"執行完成/沒有執行");
        }

    }];
    
    //執行命令
    [self.command execute:@1];

RAC高級用法

    //創建信號中信號
    RACSubject *signalOfSignals = [RACSubject subject];
    RACSubject *signalA = [RACSubject subject];
    RACSubject *signalB = [RACSubject subject];
    // 訂閱信號
//        [signalOfSignals subscribeNext:^(RACSignal *x) {
//            [x subscribeNext:^(id x) {
//                NSLog(@"%@",x);
//            }];
//        }];
//     switchToLatest:獲取信號中信號發送的最新信號
    [signalOfSignals.switchToLatest subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];
    
    // 發送信號
    [signalOfSignals sendNext:signalA];
    
    [signalA sendNext:@1];
    [signalB sendNext:@"BB"];
    [signalA sendNext:@"11"];

RACCommand:處理事件

RACCommand:不能返回一個空的信號

  // 1.創建命令
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        // input:執行命令傳入參數
        // Block調用:執行命令的時候就會調用
        NSLog(@"%@",input);
        
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
            // 發送數據
            [subscriber sendNext:@"執行命令產生的數據"];
            
            return nil;
        }];
    }];
    
    // 如何拿到執行命令中產生的數據
    // 訂閱命令內部的信號
    // 1.方式一:直接訂閱執行命令返回的信號
    // 2.方式二:
    
    // 2.執行命令
    RACSignal *signal = [command execute:@1];
    
    // 3.訂閱信號
    [signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM