1. 程式人生 > >第四十五篇:ReactiveCocoaObjC使用

第四十五篇:ReactiveCocoaObjC使用

引導:首先如果要使用 ReactiveCocoaObjC 第三方框架就先需要匯入該框架,我匯入時使用了 cocoapods 匯入的,在 podfile 檔案中輸入以下的內空:

use_frameworks!

target 'ReactiveCocoaDome' do

    pod 'AFNetworking', '~> 3.0.4'
    pod 'ReactiveObjC', '~> 3.0.0'

end

1、RACSignal 先訂閱 再發送 (主執行緒中執行)

/**
    1、RACSignal : 先訂閱 再發送(主執行緒中執行)
 */
-(void
(^)(void))RACSignalDome { return ^{ /* RACSignal : 只能訂閱且只有一個訂閱者subscriber,RACSignal 本身不能傳送訊號;在建立 RACSignal 的時候會自動建立一個 RACSubscriber 類的物件subscriber,subscriber 可以傳送訊號資訊; */ RACSignal * signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { NSLog
(@"訊號 block , thread = %@",[NSThread currentThread]); // 傳送訊號 [subscriber sendNext:@"RACSignalDome"]; // 呼叫該方法後會自動使用 disposableWithBlock() [subscriber sendCompleted]; // 釋放 return [RACDisposable disposableWithBlock:^{ NSLog
(@"disposableWithBlock , thread = %@",[NSThread currentThread]); }]; }]; // 訂閱 // 當建立 signal 時的 subscriber 發出訊號改變 [subscriber sendNext:@"中國人民"] 就會呼叫 下面的 訂閱block , 當前最新版本與以往的不同,現在都是在主執行緒中執行 block [signal subscribeNext:^(id _Nullable x) { NSLog(@"訂閱 block = %@ , thread = %@",x,[NSThread currentThread]); } error:^(NSError * _Nullable error) { NSLog(@"訂閱錯誤 = %@,thread = %@",error,[NSThread currentThread]); } completed:^{ NSLog(@"完成訂閱 thread = %@",[NSThread currentThread]); }]; [signal subscribeNext:^(id _Nullable x) { NSLog(@"subscribeNext x = %@",x); }]; }; }

2、RACSubject:先訂閱 再發送 (主執行緒中執行)

  • 價值:可以用在代理上,引數就可以可區分呼叫哪一塊的程式碼
/**
    2、RACSubject:先訂閱 再發送 (主執行緒中執行)
             價值:可以用在代理上,引數可以用於區分呼叫哪一塊的程式碼
 */
-(void (^)(void))RACSubjectDome
{
    return ^{

        RACSubject * subject = [RACSubject subject];

        // 其中所有的 block 都在 main 主執行緒中執行
        // 訂閱
        [subject subscribeNext:^(id  _Nullable x) {
            NSLog(@"subscribeNext:x = %@ , thread = %@",x,[NSThread currentThread]);
        }];

        [subject subscribeNext:^(id  _Nullable x) {
            NSLog(@"subscribeNext:x = %@ error completed , thread  = %@",x,[NSThread currentThread]);
        } error:^(NSError * _Nullable error) {
            NSLog(@"error = %@ , thread = %@",error,[NSThread currentThread]);
        } completed:^{
            NSLog(@"completed ! , thread = %@",[NSThread currentThread]);
        }];

        // 傳送
        [subject sendNext:@"RACSubjectDome"];
        [subject sendCompleted];
    };
}

3、RACReplaySubject先發送 再訂閱(主執行緒中執行)

  • 這個比較 實用 ,可以在不知道什麼時候傳送訊號的情況下準確的接收到訊號
/**
    3、先發送 再訂閱 (這個比較 實用 ,可以在不知道什麼時候傳送訊號的情況下準確的接收到訊號)(主執行緒中執行)
 */
-(void(^)(void))RACReplaySubjectDome
{
    return ^{

        // Capacity 事先預指訂閱的個數,裡面是動太陣列
        RACReplaySubject * replaySubject = [RACReplaySubject replaySubjectWithCapacity:2];

        // 傳送
        [replaySubject sendNext:@"RACReplaySubjectDome 先發送 1"];
        [replaySubject sendNext:@"RACReplaySubjectDome 先發送 2"];
        [replaySubject sendCompleted];

        // 訂閱
        [replaySubject subscribeNext:^(id  _Nullable x) {
            NSLog(@"subscribeNext:x = %@ error completed , thread  = %@",x,[NSThread currentThread]);
        } error:^(NSError * _Nullable error) {
            NSLog(@"error = %@ , thread = %@",error,[NSThread currentThread]);
        } completed:^{
            NSLog(@"completed ! , thread = %@",[NSThread currentThread]);
        }];

        // 延時訂閱,一樣可以接收到訊號
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [replaySubject subscribeNext:^(id  _Nullable x) {

                NSLog(@"subscribeNext: x = %@, thread = %@",x,[NSThread currentThread]);
            }];
        });

    };
}

4、RACTuple :將陣列 或 字典等所有的內容可以用 元組 列出(非同步執行,開了新的執行緒)

/**
    4、RACTuple :將陣列 或 字典等所有的內容可以用 元組 來列出(非同步執行,開了新的執行緒)
 */
-(void(^)(void))NSArrayAndNSDictionaryRACTupleDome
{
    return ^{

        // 1.把值包裝成 元組
        RACTuple * tuple = RACTuplePack(@"abc",@"def",@"ghj");

//        // 解析元組
//        RACTupleUnpack(NSString * a , NSString * b , NSString * c) = tuple ;

        NSLog(@"RACTuple 元組包裝: pack = %@ ",tuple);


        // 2.NSDictionary 元組 , 將字典裡面的每一對 keyValue 列舉出來(開了一個新的執行緒,非同步列舉)
        NSDictionary * dicTuple = @{@"name":@"Jakey" , @"age":@18 , @"student":@(YES)};

        [dicTuple.rac_sequence.signal subscribeNext:^(id  _Nullable x) {

            NSString * key = [(RACTuple *)x objectAtIndex:0];
            id         value = [(RACTuple *)x objectAtIndex:1];
            NSLog(@"NSDictionary 元組使用 = %@ , key = %@ , value = %@ , thread = %@",x,key,value,[NSThread currentThread]);
        }completed:^{
            NSLog(@"NSDictionary 元組使用 completed , thread = %@",[NSThread currentThread]);
        } ];


        // 3.NSArray 元組 ,將陣列內的所有資料列舉出來 (非同步列舉)
        NSArray * array = @[@"klr",@"nop",@"rst"];
        [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {

            NSLog(@"NSArray 元組 x = %@ , thread = %@",x,[NSThread currentThread]);
        }error:^(NSError * _Nullable error) {
            NSLog(@"NSArray 元組 error = %@",error);
        } completed:^{
            NSLog(@"NSArray 元組 completed ,thread = %@",[NSThread currentThread]);
        }];


        // 4.非同步列出 陣列 或 字典 內容
        NSArray * mapArray = [[array.rac_sequence map:^id _Nullable(id  _Nullable value) {

            NSLog(@"value = %@ , thread = %@",value,[NSThread currentThread]);

            return [value stringByAppendingString:@" temp"];
        }] array] ;
        NSLog(@"===== %@", mapArray);
    };
}

5、RACMulticastConnection :廣播連線(將 RACSignal 轉成 RACMulticastConnection , block 在 main 主執行緒執行)

/**
    5、RACMulticastConnection :廣播連線(將 RACSignal 轉成 RACMulticastConnection , block 在 main 主執行緒執行)
 */
-(void(^)(void))RACMulticastConnectionDome
{
    return ^{

        // 不能解決 _view ( === self->_view , 這樣就無法解決強引用的問題)
        // __weak typeof(self) weakSelf = self ;

        // 無論哪種用法都可以解決強引用問題
        @weakify(self);

        RACSignal * signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

            // @weakify(self) 配套使用
            @strongify(self);

            NSLog(@"connection createSignal , thread = %@",[NSThread currentThread]);

            [self loadDataFromNetwork:^(NSArray *dataArr) {

                NSLog(@"loadDataFromNetwork block dataArr = %@ , thread = %@",dataArr , [NSThread currentThread]);

                // 傳送訊號
                [subscriber sendNext:dataArr];
                [subscriber sendCompleted];
            }];

            return [RACDisposable disposableWithBlock:^{

                NSLog(@"connection disposableWithBlock ,thread = %@",[NSThread currentThread]);
            }];
        }];

//        // 直接訂閱
//        [signal subscribeNext:^(id  _Nullable x) {
//           
//            NSLog(@"subscribeNext x = %@ , thread = %@",x,[NSThread currentThread]);
//            
//        }];


        // 將 signal 轉化成 connection
        RACMulticastConnection * connection = [signal publish];

        // 訂閱訊號
        // RACSubject:RACSubscriber
        [connection.signal subscribeNext:^(id  _Nullable x) {

            NSLog(@"commection x = %@ , thread = %@",x,[NSThread currentThread]);
        }];
        [connection.signal subscribeNext:^(id  _Nullable x) {

            NSLog(@"commection2 x = %@ , thread = %@",x,[NSThread currentThread]);
        }];

        // 連線
        // RACSubject 訂閱 RACSignal
        [connection connect];

    };
}

// 網路資料載入方法
-(void)loadDataFromNetwork:(void(^)(NSArray * dataArr))resultBlock
{
    NSLog(@"loadDataFromNetwork selector thread = %@",[NSThread currentThread]);

    resultBlock(@[@"temp = 1" , @"temp = 2" , @"temp = 3"]);
}

6、RACCommand:處理事件的操作.(主執行緒中執行)

  • (1) RACCommand : 內部必須返回 RACSignal
  • (2) executionSignals : 訊號外的訊號
    • (2.1) switchToLatest 最新發出來訊號的 RACSignal 型別
    • (2.2) 能過 (2.1)的詮釋,那麼只要用 switchToLatest subscribeNext: 訂閱,就可以接收到發出來的訊號
  • (3) 下面是執行的順序,用 (index)表示
  • (4) execute:(id)input ; 該物件方法必須被呼叫(呼叫次數只有一次有效)才會執行一些相關操作,所有的 block 執行操作的 入口
/**
    6、RACCommand:處理事件的操作.(主執行緒中執行)
        (1) RACCommand : 內部必須返回 RACSignal
        (2) executionSignals : 訊號外的訊號
             (2.1) switchToLatest 最新發出來訊號的 RACSignal 型別
             (2.2) 能過 (2.1)的詮釋,那麼只要用 switchToLatest subscribeNext: 訂閱,就可以接收到發出來的訊號
        (3) 下面是執行的順序,用 (index)表示
        (4) execute:(id)input ; 該物件方法必須被呼叫(呼叫次數只有一次有效)才會執行一些相關操作,所有的 block 執行操作的 入口
 */
-(void(^)(void))RACCommandDome
{
    return ^{

        RACCommand * command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
           // input 即是執行 execute:(id)input; 傳進來的值   (3)
            NSLog(@"init RACCommand block 被執行 initWithSignalBlock input = %@ , thread = %@",input,[NSThread currentThread]);

            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
               // (6)
                NSLog(@"內部建立的訊號block 被執行 createSignal , thread = %@",[NSThread currentThread]);

                // 傳送訊號
                [subscriber sendNext:@"create Signal for somthing %@"];
                [subscriber sendCompleted];

                return [RACDisposable disposableWithBlock:^{
                    // 當 [subscriber sendCompleted] 呼叫時就會執行釋放功能的 block (8)
                    NSLog(@"內部訊號被釋放 disposableWithBlock , thread = %@",[NSThread currentThread]);
                }];

            }];
        }];

        // 訂閱最新發出來訊號的 signal (7)
        [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {

            NSLog(@"執行最近的 signal , x = %@ , thread = %@",x,[NSThread currentThread]);
        }];

        // executionSignals 這裡傳的 x 值型別為 RACDynamicSignal 型別物件 (5)
        [command.executionSignals subscribeNext:^(id  _Nullable x) {

            NSLog(@"executionSignals subscribeNext x = %@ , thread = %@",x,[NSThread currentThread]);
        }];

        // 檢視將要執行,每執行完一個步聚 都會呼叫一次檢視哪個 signal block(即 第 x 個 block  ) 將被使用 (2)(4)(9)
        // signal 的 skip: 方法功能是跳過 skipCount 個 使用 block 的檢視
        [[[command executing] skip:0] subscribeNext:^(NSNumber * _Nullable x) {
            NSLog(@"executing signal subscribeNext x = %@ , thread = %@",x,[NSThread currentThread]);
        }];


        // 只執行一次 (1)
        [command execute:@"execute"];

//        [command execute:@"execute"];

    };
}

7、NSObject 分類中 rac_liftSelector… 方法的使用(即等待成所有的 RACSignal 物件傳送完訊號再執行方法) (主程中執行)

/**
    7、NSObject 分類中 rac_liftSelector... 方法的使用(即等待成所有的 RACSignal 物件傳送完訊號再執行方法) (主程中執行)
 */
-(void(^)(void))rac_liftSelectorDome
{
    return ^{

        RACSignal * signalOne = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

            // 現在想發出訊號了
            [subscriber sendNext:@"網路請求資料 1"];

            // 不需要釋放操作
            return nil ;
        }];

        RACSignal * signalTwo = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

            // 現在想發出訊號了
            [subscriber sendNext:@"網路請求資料 2"];

            // 不需要釋放操作
            return nil ;
        }];

        [self rac_liftSelector:@selector(updateUIWithSignalOneMessage:signalTwoMessage:) withSignalsFromArray:@[signalOne , signalTwo]];
    };
}

// 當據有資料都拿到手後更新UI , 傳的資料就是 signalOne 和 signalTwo 發出來的訊號資料 ,(所以當前設計的接收方法 也必需要有兩個引數,發出的訊號按順序 傳參)
// 假如當前物件方法只設計 傳一個引數,那麼就會導致崩潰
-(void)updateUIWithSignalOneMessage:(id)signalOneMessage signalTwoMessage:(id)signalTwoMessage
{
    NSLog(@"signalOneMessage = %@ , signalTwoMessage = %@ , thread = %@",signalOneMessage,signalTwoMessage,[NSThread currentThread]);
}

8、NSNotificationCenter : 使用了 RAC 把監聽通知的方法改成了 block 形勢

/**
    8、NSNotificationCenter : 使用了 RAC 把監聽通知的方法改成了 block 形勢
 */
-(void(^)(void))RAC_Notification_Dome
{
    return ^{

        // 監聽通知
        [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidHideNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
            NSLog(@"NSNotification 1 x = %@",x.userInfo);
        }];

        [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidHideNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
            NSLog(@"NSNotification 2 x = %@",x.userInfo);
        }];

        // 發出通知
        [[NSNotificationCenter defaultCenter] postNotificationName:UIKeyboardDidHideNotification object:nil];

    };
}

9、RAC UITextField 監聽 text 變化 和 繫結 lable.text 永遠等於 textField.text

/**
    9、RAC UITextField 監聽 text 變化 和 繫結 lable.text 永遠等於 textField.text
 */
-(void(^)(void))RAC_UITextField_Dome
{
    // 解決 block 迴圈引用問題 , 與 @strongify(self); 配套使用
    @weakify(self);
    return ^{

        @strongify(self);

        // UITextField RAC使用
        UITextField * textField = [[UITextField alloc] initWithFrame:CGRectMake(100, 100, 100, 50)];
        textField.backgroundColor = [UIColor redColor];
        [self.view addSubview:textField];

        [[textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"text = %@ , textField.text = %@ , thread = %@",x,textField.text,[NSThread currentThread]);
        }];


        // 繫結 lable.text
        UILabel * lable = [[UILabel alloc] initWithFrame:CGRectMake(100, 200, 100, 50)];
        lable.backgroundColor = [UIColor greenColor];
        [self.view addSubview:lable];

        // 繫結 lable.text 永遠等於 textField.text
        RAC(lable,text) = textField.rac_textSignal ;


    };
}

10、RAC KVO 監聽屬性內容變化

/**
    10、RAC KVO 監聽屬性內容變化
 */
-(void(^)(void))RAC_KVO_Dome
{
    return ^{

        [RACObserve(self, age) subscribeNext:^(id  _Nullable x) {

            NSLog(@"KVO 監聽到 age 內容發生變化 ,變為 %@ , thread = %@",x,[NSThread currentThread]);
        }];
    };
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    self.age++ ;
}

11、RACSignal 的 bind 繫結方法

/**
    11、RACSignal 的 bind 繫結方法
 */
-(void(^)(void))RACSignalBind
{
    return ^{

        UITextField * textField = [[UITextField alloc] init];

        // bind 裡面的做法:
        // (1) 建立一個 RACSignal 物件做為 bind 方法的返值;
        // (2) 當我們拿到該返的 RACSignal 類物件 tempSignal 去進行訂閱;
        // (3) 然後會執行建立 RACSignal 物件時的 block A ,並在裡面執行 bindBlock 拿到 返回的 block (B返回值為 RACSignal 物件);
        // (4) 再執行 block B 就拿到 RACReturnSignal 物件;
        // (5) RACReturnSignal 物件 進行訂閱,然後在該訂閱 block 裡面拿到 value 值;
        // (6) tempSignal 的 subscriber 訂閱者 傳送訊號值 value , 最後在外面 tempSignal 物件的訂閱就接收到資訊了。
        [[textField.rac_textSignal bind:^RACSignalBindBlock _Nonnull{

            NSLog(@"bind block");

            return ^RACSignal * (id _Nullable value, BOOL *stop){

                NSLog(@"return block value = %@ can do somthing",value);

                return [RACReturnSignal return:value];
            };

        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"signal subscribeNext x = %@",x);
        }];
    };
}

12、RACReplaySubject 物件的 then: 方法用法

  • then 功能:可以使 RACSignal 及其子類的 物件有序接收訊號
/**
    12、RACReplaySubject 的 then: 方法用法。
        then 功能:可以使 RACSignal 及其子類的 物件有序接收訊號
 */
-(void(^)(void))RACReplaySubjectThenUseDome
{
    return ^{

        // 在這裡儘量使用 RACReplaySubject 類 ,因為 RACReplaySubject 可以先發送訊號,訂閱程式碼可以放在之後寫。
        // 如果 使用 RACSignal 或 RACSubject ,那麼必須要等這些物件訂閱完後,傳送的訊號才能接收的到
        RACReplaySubject * subjectA = [RACReplaySubject subject];

        // 這就是好處,先發送
        [subjectA sendNext:@"AA"];
        // 必須要呼叫這個方法才會來到 then 後的 block
        [subjectA sendCompleted];

        // 按指定的順序接收到訊號        
        [[[subjectA then:^RACSignal * _Nonnull{

            // 當 subjectA 傳送訊號完成後 才執行 當前的 block
            RACReplaySubject * subjectB = [RACReplaySubject subject];

            // 可以單獨呼叫傳送訊號完成的方法就可以接著執行下一個 then 
            [subjectB sendCompleted];

            return subjectB ;

        }] then:^RACSignal * _Nonnull{

            // 當 subjectB 傳送訊號完成後 才執行 當前的 block
            RACReplaySubject * subjectC = [RACReplaySubject subject];

            // subjectC 傳送訊號
            [subjectC sendNext:@"CC"];

            return subjectC ;

        }] subscribeNext:^(id  _Nullable x) { // 這個就 "相當於" 訂閱了 subjectC 物件(但真正的物件則不是 subjectC 物件) ,x = @"CC"
            NSLog(@"RACReplaySubject C x = %@",x);
        }];

    };
}

13、合併兩個及以上 RACSignal 或 RACSignal 的子類物件,用新建立的 RACSignal 物件接收多個 RACSignal 或 RACSignal 的子類物件 發出的訊號

  • 只要求其中任一的一個被合併物件傳送訊號就能收到
/**
    13、合併兩個及以上 RACSignal 或 RACSignal 的子類物件,用新建立的 RACSignal 物件接收多個 RACSignal 或 RACSignal 的子類物件 發出的訊號 (只要求其中任一的一個被合併物件傳送訊號就能收到)
 */
-(void(^)(void))RACSignalMergeDome
{
    return ^{

        RACReplaySubject * subjectA = [RACReplaySubject subject];
        RACReplaySubject * subjectB = [RACReplaySubject subject];
        RACReplaySubject * subjectC = [RACReplaySubject subject];

        // 三個物件傳送訊號(只需其中一個或多個傳送訊號時,合併的 訊號物件 都可以在訂閱的 block 接收到資訊)
        [subjectB sendNext:@"BB"];
        [subjectA sendNext:@"AA"];
        [subjectC sendNext:@"CC"];

        // 合併兩個訊號物件變成一個接收訊號物件 subjectD , subjectD 訂閱 接收 subjectB 和 subjectA 傳送的訊號
        [[[subjectA merge:subjectB] merge:subjectC] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];

    };
}

14、壓縮兩個及以上 RACSignal 或 RACSignal 的子類物件,用新建立的 RACSignal 物件同時接收多個 RACSignal 或 RACSignal 的子類物件 發出的訊號

  • 必須所有 的被壓縮物件 一起傳送訊號 才能收到
  • 注意:解析時需要一層一層的解析
/**
    14、壓縮兩個及以上 RACSignal 或 RACSignal 的子類物件,用新建立的 RACSignal 物件同時接收多個 RACSignal 或 RACSignal 的子類物件 發出的訊號 (必須所有 的被壓縮物件 一起傳送訊號 才能收到)(注意:解析時需要一層一層的解析)
 */
-(void(^)(void))RACSignalZipWithDome
{
    return ^{

        RACReplaySubject * subjectA = [RACReplaySubject subject];
        RACReplaySubject * subjectB = [RACReplaySubject subject];
        RACReplaySubject * subjectC = [RACReplaySubject subject];

        // 三個物件同時傳送訊號,缺一不可
        [subjectA sendNext:@"AA"];
        [subjectB sendNext:@"BB"];
        [subjectC sendNext:@"CC"];

        // 合併兩個訊號物件變成一個接收訊號物件 subjectD , subjectD 訂閱 接收 subjectB 和 subjectA 傳送的訊號
        // x 型別為 元組 RACTwoTuple 型別:解析使用
        [[[subjectA zipWith:subjectB] zipWith:subjectC] subscribeNext:^(id  _Nullable x) {

            // 注意:元組需要 一層一層 地解析
            RACTupleUnpack(RACTuple * AB , NSString * C) = x ;

            RACTupleUnpack(NSString * A , NSString * B) = AB ;

            NSLog(@"A = %@ , B = %@ , C = %@",A , B , C);
        }];

    };
}

15、合併兩個及以上 RACSignal 或 RACSignal 的子類物件,用新建立的 RACSignal 物件 同時接收多個 RACSignal 或 RACSignal 的子類物件 發出的訊號

  • 任意一個 被合併的物件 傳送的訊號 都能收到
  • 注意:解析時需要一層一層的解析
/**
    15、合併兩個及以上 RACSignal 或 RACSignal 的子類物件,用新建立的 RACSignal 物件 同時接收多個 RACSignal 或 RACSignal 的子類物件 發出的訊號 (任意一個 被合併的物件 傳送的訊號 都能收到)(注意:解析時需要一層一層的解析)
 */
-(void(^)(void))RACSignalCombineLatestWithDome
{
    return ^{

        RACReplaySubject * subjectA = [RACReplaySubject subject];
        RACReplaySubject * subjectB = [RACReplaySubject subject];
        RACReplaySubject * subjectC = [RACReplaySubject subject];

        // 三個物件同時傳送訊號,缺一不可
        [subjectA sendNext:@"AA"];
        [subjectB sendNext:@"BB"];
        [subjectC sendNext:@"CC"];

        // 1.合併三個訊號物件變成一個接收訊號物件 subjectD , subjectD 訂閱 接收 subjectB 和 subjectA 傳送的訊號
        // x 型別為 元組 RACTwoTuple 型別:解析使用
        [[[subjectA combineLatestWith:subjectB] combineLatestWith:subjectC] subscribeNext:^(id  _Nullable x) {

            // 注意:元組需要 一層一層 地解析
            RACTupleUnpack(RACTuple * AB , NSString * C) = x ;

            RACTupleUnpack(NSString * A , NSString * B) = AB ;

            NSLog(@"A = %@ , B = %@ , C = %@",A , B , C);
        }];


        // 2.也可以把那些訊號傳的引數聚合成一個值
        //   遵守 NSFastEnumeration 協議的類都可成為陣列
        //   reduce block 引數可以自己根據訊號設定
        [[RACSignal combineLatest:@[subjectB,subjectA,subjectC] reduce:^id (NSString * signalA,NSString * signalB,NSString * signalC){

            // 把這 三個中任意 一個發出的訊號值 聚合成一個值 NSString 型別

            return [NSString stringWithFormat:@"A = %@ , B = %@ , C = %@",signalA , signalB , signalC];

        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"聚合後三個值變成一個 NSString 型別的值: %@",x);
        }];


        // 3.也可以用聚合繫結做法
        UILabel * lable = [[UILabel alloc] init];

        RAC(lable , text) = [RACSignal combineLatest:@[subjectB,subjectA,subjectC] reduce:^id (NSString * signalA,NSString * signalB,NSString * signalC){

            // 把這 三個中任意 一個發出的訊號值 聚合成一個值 NSString 型別

            return [NSString stringWithFormat:@"A = %@ , B = %@ , C = %@",signalA , signalB , signalC];

        }];

        NSLog(@"lable.text = %@",lable.text);
    };
}

16、RACSignal 的 map 攔截訊號發出的訊號和處理資料

/**
    16、RACSignal 的 map 攔截訊號發出的訊號和處理資料
 */
-(void(^)(void))RACSignalMapDome
{
    return ^{

        RACReplaySubject * signal = [RACReplaySubject subject];

        [[signal map:^id _Nullable(id  _Nullable value) {
            return [NSString stringWithFormat:@"%@ (攔截髮出的訊號,拼接個想要的東西)",value];
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"接收到處理後的資訊 = %@",x);
        }];

        [signal sendNext:@"@(當前訊號的資訊)"];

    };
}

17、訊號中的訊號,RACSignal 的 flattenMap 物件方法,用來接收訊號物件value 和 訊號物件value發出的資訊

/**
    17、訊號中的訊號,RACSignal 的 flattenMap 物件方法,用來接收訊號物件value 和 訊號物件value發出的資訊
 */
-(void(^)(void))RACSignalFlattenMapDome
{
    return ^{

        RACReplaySubject * signal = [RACReplaySubject subject];
        RACSubject * subject = [RACSubject subject];

        [[signal flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {

            // value 是訊號物件 == subject

            return [value map:^id _Nullable(id  _Nullable value) {

                return [NSString stringWithFormat:@"(新增攔截訊號處理訊號) (%@)",value];
            }];
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"接收到處理後的資訊 = %@",x);
        }];

        [signal sendNext:subject];

        [subject sendNext:@" subject 發出訊號了"];
    };
}

18、訊號過濾器:RACSignal 的 filter: 方法,用來設定一個條件發出的訊號才會被接收到

/**
    18、訊號過濾器:RACSignal 的 filter: 方法,用來設定一個條件發出的訊號才會被接收到
 */
-(void(^)(void))RACSignalFilterDome
{
    return ^{

        RACReplaySubject * replaySubject = [RACReplaySubject replaySubjectWithCapacity:1];

        // filter block 是一個過濾器,只有滿足條件發出的訊號才會被接收到
        [[replaySubject filter:^BOOL(id  _Nullable value) {

            return ((NSString *)value).length >= 6 ;

        }] subscribeNext:^(id  _Nullable x) {

            NSLog(@"接收到訊號 = %@",x);
        }];

//        // 發出的訊號長度為 5 時,訂閱收不到訊號資訊
//        [replaySubject sendNext:@"12345"];

        // 發出的訊號長度為 >= 6 時,訂閱收到訊號了資訊
        [replaySubject sendNext:@"123456"];

    };
}

19、RACSignal 訊號物件 與 定時器的關係

/**
    19、RACSignal 訊號物件 與 定時器的關係
 */
-(void(^)(void))RACSignalAndTimerDome
{
    return ^{

        // 1、定時器
        // TimeInterval : 間隔時間,秒
        // repeats      : 是否重複
        // blokc        : 呼叫程式碼塊 (在主執行緒中執行)
        [NSTimer scheduledTimerWithTimeInterval:1 repeats:NO block:^(NSTimer * _Nonnull timer) {
            NSLog(@"每隔一秒呼叫一次當前 block , thread = %@",[NSThread currentThread]);
        }];



        // 2、RACSignal 制定定時器
        // interval : 間隔的時間,秒
        /**
            onScheduler : 多執行緒 , 佇列
                mainThreadScheduler : 主執行緒中 執行訂閱程式碼塊
                currentScheduler    : 在當前建立時的執行緒中 執行訂閱程式碼塊
         */
        /**
            schedulerWithPriority: 或 schedulerWithPriority:name: 優先順序 , name 表示執行緒名
                // 優先順序高,開起 新的執行緒
                RACSchedulerPriorityHigh        = DISPATCH_QUEUE_PRIORITY_HIGH,
                // 預設優先順序,開起 新的執行緒
                RACSchedulerPriorityDefault     = DISPATCH_QUEUE_PRIORITY_DEFAULT,
                // 優先順序低,開起 新的執行緒
                RACSchedulerPriorityLow         = DISPATCH_QUEUE_PRIORITY_LOW,
                // app 進入後臺也可以呼叫,開起 新的執行緒
                RACSchedulerPriorityBackground  = DISPATCH_QUEUE_PRIORITY_BACKGROUND,
         */

        [[RACReplaySubject interval:1 onScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityLow]] subscribeNext:^(NSDate * _Nullable x) {
            // x 是當前的時間
            NSLog(@"每隔一秒呼叫訂閱程式碼:x = %@ , 線上程 thread = %@",x,[NSThread currentThread]);
        }];



        // 3、使用 RACSignal 訊號 延時
        [[[RACReplaySubject createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

            [subscriber sendNext:@"2秒後呼叫訂閱 block"];

            return nil ;
        }] delay:5] // 延時 5秒 再
         subscribeNext:^(id  _Nullable x) {

             NSLog(@"%@",x);
        }];

    };
}