ReactiveCocoa(FRP)-進階篇

  • 1.ReactiveCocoa常見操作方法介紹

    • 1.1 ReactiveCocoa操作須知

      所有的信號(RACSignal)都可以進行操作處理,因為所有操作方法都定義在RACStream.h中,而RACSignal繼承RACStream。

  • 1.2. ReactiveCocoa操作思想

    - 運用的是Hook(鉤子)思想,Hook是一種用于改變API(應用程序編程接口:方法)執行結果的技術.
    - Hook用處:截獲API調用的技術。
    - Hook原理:在每次調用一個API返回結果之前,先執行你自己的方法,改變結果的輸出。
    - RAC開發方式:RAC中核心開發方式,也是**綁定**,之前的開發方式是賦值,而用RAC開發,應該把重心放在綁定,也就是可以在創建一個對象的時候,就綁定好以后想要做的事情,而不是等賦值之后在去做事情。
    - 列如:把數據展示到控件上,之前都是重寫控件的setModel方法,用RAC就可以在一開始創建控件的時候,就綁定好數據。
    
  • 1.3 ReactiveCocoa核心方法bind

    • 1.3.1.ReactiveCocoa操作的核心方法是bind(綁定),給RAC中的信號進行綁定,只要信號一發送數據,就能監聽到,從而把發送數據改成自己想要的數據。

    • 1.3.2.在開發中很少使用bind方法,bind屬于RAC中的底層方法,RAC已經封裝了很多好用的其他方法,底層都是調用bind,用法比bind簡單.

    • 1.3.3.bind方法簡單介紹和使用。


      bind方法簡單介紹和使用

      假設想監聽文本框的內容,并且在每次輸出結果的時候,都在文本框的內容拼接一段文字“輸出:”

      方式一:在返回結果后,拼接。
      [_textField.rac_textSignal subscribeNext:^(id x) {

        NSLog(@"輸出:%@",x);
      
        }];
      

      方式二:在返回結果前,拼接,使用RAC中bind方法做處理。

       bind方法參數:需要傳入一個返回值是RACStreamBindBlock的block參數
       RACStreamBindBlock是一個block的類型,返回值是信號,參數(value,stop),因此參數的block返回值也是一個block。
      
      RACStreamBindBlock:
      參數一(value):表示接收到信號的原始值,還沒做處理
      參數二(*stop):用來控制綁定Block,如果*stop = yes,那么就會結束綁定。
      返回值:信號,做好處理,在通過這個信號返回出去,一般使用RACReturnSignal,需要手動導入頭文件RACReturnSignal.h。
      

    bind方法使用步驟:

    • 1.傳入一個返回值RACStreamBindBlock的block。
    • 2.描述一個RACStreamBindBlock類型的bindBlock作為block的返回值。
    • 3.描述一個返回結果的信號,作為bindBlock的返回值。
      注意:在bindBlock中做信號結果的處理。

    底層實現:

    • 1.源信號調用bind,會重新創建一個綁定信號。
    • 2.當綁定信號被訂閱,就會調用綁定信號中的didSubscribe,生成一個bindingBlock。
    • 3.當源信號有內容發出,就會把內容傳遞到bindingBlock處理,調用bindingBlock(value,stop)
    • 4.調用bindingBlock(value,stop),會返回一個內容處理完成的信號(RACReturnSignal)。
    • 5.訂閱RACReturnSignal,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。

    注意:不同訂閱者,保存不同的nextBlock,看源碼的時候,一定要看清楚訂閱者是哪個。
    這里需要手動導入#import <ReactiveCocoa/RACReturnSignal.h>,才能使用RACReturnSignal

    [[_textField.rac_textSignal bind:^RACStreamBindBlock{
    
    // 什么時候調用:
    // block作用:表示綁定了一個信號.
    
    return ^RACStream *(id value, BOOL *stop){
    
        // 什么時候調用block:當信號有新的值發出,就會來到這個block。
    
        // block作用:做返回值的處理
    
        // 做好處理,通過信號返回出去.
        return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
    };
    
    }] subscribeNext:^(id x) {
    
          NSLog(@"%@",x);
    
    }];
    
  • 1.4ReactiveCocoa操作方法之映射(flattenMap,Map)

    flattenMap,Map用于把源信號內容映射成新的內容

    • 1.4.1. flattenMap 用于信號中的信號,把源信號的內容映射成一個新的信號,信號可以是任意類型

      flattenMap使用步驟:
      1.傳入一個block,block類型是返回值RACStream,參數value
      2.參數value就是源信號的內容,拿到源信號的內容做處理
      3.包裝成RACReturnSignal信號,返回出去。
      flattenMap底層實現:
      0.flattenMap內部調用bind方法實現的,flattenMap中block的返回值,會作為bind中bindBlock的返回值。
      1.當訂閱綁定信號,就會生成bindBlock。
      2.當源信號發送內容,就會調用bindBlock(value, *stop)
      3.調用bindBlock,內部就會調用flattenMap的block,flattenMap的block作用:就是把處理好的數據包裝成信號。
      4.返回的信號最終會作為bindBlock中的返回信號,當做bindBlock的返回信號。
      5.訂閱bindBlock的返回信號,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。

      代碼:

      //1.創建信號
        RACSubject *subject =[RACSubject subject];
      //2.綁定信號
        [[subject flattenMap:^RACStream *(id value) {
      // block 只要原信號發送內容就會調用
      // value 就是源信號發送的內容
      
         NSLog(@"11---%@",[NSThread currentThread]);
      
         value = [NSString stringWithFormat:@"JK %@",value];
      
       // 返回信號用來包裝成修改內容的值
           return [RACReturnSignal return:value];
       }] subscribeNext:^(id x) {
      
       //3.訂閱信號,flattenMap中返回的是什么信號,訂閱的就是什么信號
      
         NSLog(@"%@",x);
      
          NSLog(@"22---%@",[NSThread currentThread]);
      
       }];
      
       //4.源信號發送信號
      
       [subject sendNext:@"123"];
      
flattenMap
  • 1.4.2.map簡單使用
    Map作用:把源信號的值映射成一個新的值

    map使用步驟:

    • 1.傳入一個block,類型是返回對象,參數是value
    • 2.value就是源信號的內容,直接拿到源信號的內容做處理
    • 3.把處理好的內容,直接返回就好了,不用包裝成信號,返回的值,就是映射的值。

    Map底層實現:

    • 0.Map底層其實是調用flatternMap,Map中block中的返回的值會作為flatternMap中block中的值。
    • 1.當訂閱綁定信號,就會生成bindBlock。
    • 2.當源信號發送內容,就會調用bindBlock(value, *stop)
    • 3.調用bindBlock,內部就會調用flattenMap的block
    • 4.flattenMap的block內部會調用Map中的block,把Map中的block返回的內容包裝成返回的信號。
    • 5.返回的信號最終會作為bindBlock中的返回信號,當做bindBlock的返回信號。
    • 6.訂閱bindBlock的返回信號,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。

    代碼

    //1.創建信號
        RACSubject *subject =[RACSubject subject];
    //2.綁定信號
        [[subject map:^id(id value) {
    
    // 返回的內容就是你要映射的值
       value = [NSString stringWithFormat:@"JK:%@",value];
    
       return value;
    
       }]subscribeNext:^(id x) {
    
          NSLog(@"映射之后的值= %@",x);
    
       }];
    
     //4.源信號發送信號
     [subject sendNext:@"123"];
    
Map簡單使用
  • 1.4.3.FlatternMap和Map的區別

    • 1.FlatternMap中的Block返回信號。
    • 2.Map中的Block返回對象。
    • 3.開發中,如果信號發出的值不是信號,映射一般使用Map
    • 4.開發中,如果信號發出的值是信號,映射一般使用FlatternMap。
  • 1.4.3.總結:signalOfsignals(信號中的信號)用FlatternMap**

     // 創建信號中的信號
     RACSubject *signalOfsignals = [RACSubject subject];
     RACSubject *signal = [RACSubject subject];
     // 創建訂閱者
     [[signalOfsignals flattenMap:^RACStream *(id value) {
    
         // 當signalOfsignals的signals發出信號才會調用
    
        return value;
    
     }] subscribeNext:^(id x) {
    
      // 只有signalOfsignals的signal發出信號才會調用,因為內部訂閱了bindBlock中返回的信號,也就是flattenMap返回的信號。
      // 也就是flattenMap返回的信號發出內容,才會調用。
    
       NSLog(@"%@aaa",x);
    }];
    
    // 信號的信號發送信號
     [signalOfsignals sendNext:signal];
    
    // 信號發送內容
    [signal sendNext:@1];
    
signalOfsignals(*信號中的信號*)用FlatternMap**
  • 1.4.4.信號中的信號創建訂閱者的3種方式
信號中的信號創建訂閱者的3種方式
// 1.創建信號中的信號
RACSubject *signalOfsignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];

//2.創建訂閱者的種方式

   //第1種方式
[signalOfsignals.switchToLatest subscribeNext:^(id x) {
    
    
    NSLog(@"%@",x);
}];

// 第2種方式
[signalOfsignals subscribeNext:^(RACSignal *x) {
    
    [x subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
        
    }];
    
}];

 // 第3種方式
[[signalOfsignals flattenMap:^RACStream *(id value) {
    
    // 當signalOfsignals的signals發出信號才會調用
    
    return value;
    
}] subscribeNext:^(id x) {
    
    // 只有signalOfsignals的signal發出信號才會調用,因為內部訂閱了bindBlock中返回的信號,也就是flattenMap返回的信號。
    // 也就是flattenMap返回的信號發出內容,才會調用。
    
    //5.打印信號發送的內容(可以進行修改)
    
    NSLog(@"%@aaa",x);
}];

// 3.信號的信號發送信號
[signalOfsignals sendNext:signal];

// 4.信號發送內容
[signal sendNext:@1];
  • 1.5 ReactiveCocoa操作方法之組合

    • 1.5.1.concat:按一定順序拼接信號,當多個信號發出的時候,有順序的接收信號。

      RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
      [subscriber sendNext:@1];
      
      [subscriber sendCompleted];
      
         return nil;
      }];
      RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
         [subscriber sendNext:@2];
      
         return nil;
      }];
      
      // 把signalA拼接到signalB后,signalA發送完成,signalB才會被激活。
      RACSignal *concatSignal = [signalA concat:signalB];
      
      // 以后只需要面對拼接信號開發。
      // 訂閱拼接的信號,不需要單獨訂閱signalA,signalB
      // 內部會自動訂閱。
      // 注意:第一個信號必須發送完成,第二個信號才會被激活
      [concatSignal subscribeNext:^(id x) {
      
           NSLog(@"%@",x);
      
      }];
      

    注意:第一個信號必須發送完成[subscriber sendCompleted];,第二個信號才會被激活,按順序打印出來.(前后按順序實現,打印)

    concat底層實現:

      -  1.當拼接信號被訂閱,就會調用拼接信號的didSubscribe
      -  2.didSubscribe中,會先訂閱第一個源信號(signalA)
      -  3.會執行第一個源信號(signalA)的didSubscribe
      -  4.第一個源信號(signalA)didSubscribe中發送值,就會調用第一個源信號(signalA)訂閱者的nextBlock,通過拼接信號的訂閱者把值發送出來.
      -  5.第一個源信號(signalA)didSubscribe中發送完成,就會調用第一個源信號(signalA)訂閱者的completedBlock,訂閱第二個源信號(signalB)這時候才激活(signalB)。
      -  6.訂閱第二個源信號(signalB),執行第二個源信號(signalB)的didSubscribe
      -  7.第二個源信號(signalA)didSubscribe中發送值,就會通過拼接信號的訂閱者把值發送出來.
    
    • 1.5.2.then:用于連接兩個信號,當第一個信號完成,才會連接then返回的信號

      then:用于連接兩個信號,當第一個信號完成,才會連接then返回的信號
      注意使用then,之前信號的值會被忽略掉.(忽略掉前面的,只打印后面的)
      底層實現:
      1、先過濾掉之前的信號發出的值。
      2、使用concat連接then返回的信號。

      then代碼
      [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

          [subscriber sendNext:@1];
          [subscriber sendCompleted];
         return nil;
       }] then:^RACSignal *{
           return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@2];
        return nil;
         }];
       }] subscribeNext:^(id x) {
      
         // 只能接收到第二個信號的值,也就是then返回信號的值
         NSLog(@"%@",x);
      }];
      
    • 1.5.3.merge:把多個信號合并為一個信號,任何一個信號有新值的時候就會調用

      merge:把多個信號合并成一個信號(沒有順序)

      //創建多個信號
      RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
         [subscriber sendNext:@1];
      
         return nil;
      }];
      
      RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
         [subscriber sendNext:@2];
      
         return nil;
      }];
      
      // 合并信號,任何一個信號發送數據,都能監聽到.
      RACSignal *mergeSignal = [signalA merge:signalB];
      
          [mergeSignal subscribeNext:^(id x) {
      
          NSLog(@"%@",x);
      
      }];
      

    merge底層實現:
    - 1.合并信號被訂閱的時候,就會遍歷所有信號,并且發出這些信號。
    - 2.每發出一個信號,這個信號就會被訂閱
    - 3.也就是合并信號一被訂閱,就會訂閱里面所有的信號。
    - 4.只要有一個信號被發出就會被監聽。

    • 1.5.4.zipWith:把兩個信號壓縮成一個信號,只有當兩個信號同時發出信號內容時,并且把兩個信號的內容合并成一個元組,才會觸發壓縮流的next事件::----夫妻關系

       RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
           [subscriber sendNext:@1];
           return nil;
        }];
      
       RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
            [subscriber sendNext:@2];
            return nil;
       }];
      
      // 壓縮信號A,信號B
      RACSignal *zipSignal = [signalA zipWith:signalB];
      
      [zipSignal subscribeNext:^(id x) {
      
           NSLog(@"%@",x);
      }];
      
zipWith
底層實現:
  - 1.定義壓縮信號,內部就會自動訂閱signalA,signalB
  - 2.每當signalA或者signalB發出信號,就會判斷signalA,signalB有沒有發出個信號,有就會把最近發出的信號都包裝成元組發出。
  • 1.5.5.combineLatest(重要)
    combineLatest:將多個信號合并起來,并且拿到各個信號的最新的值,必須每個合并的signal至少都有過一次sendNext,才會觸發合并的信號

    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    
       [subscriber sendNext:@1];
    
       return nil;
    }];
    
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    
       [subscriber sendNext:@2];
    
       return nil;
    }];
    
    // 把兩個信號組合成一個信號,跟zip一樣,沒什么區別
    RACSignal *combineSignal = [signalA combineLatestWith:signalB];
    
       [combineSignal subscribeNext:^(id x) {
    
        NSLog(@"%@",x);
    }];
    

    combineLatest底層實現

    • 1.當組合信號被訂閱,內部會自動訂閱signalA,signalB,必須兩個信號都發出內容,才會被觸發。
    • 2.并且把兩個信號組合成元組發出。


      combineLatest
  • 1.6 ReactiveCocoa操作方法之過濾

    • 1.6.1.filter過濾信號,使用它可以獲取滿足條件的信號
    filter過濾信號
        #pragma mark filter
        -(void)filter
        {
             // 過濾:
            // 每次信號發出,會先執行過濾條件判斷.
           [[_textField.rac_textSignal filter:^BOOL(NSString *value) {
               return value.length > 3;
           }] subscribeNext:^(id x) {
      
              NSLog(@"%@",x);
           }];
        }
    
    • 1.6.2.ignore:忽略完某些值的信號
    ignore:忽略完某些值的信號
      // ignore: 忽略一些值
      // ignoreValues: 忽略所有的值
    
      //1.創建信號
      RACSubject *subject = [RACSubject subject];
    
      //2.忽略一些
      RACSignal *ignoreSignal = [subject ignore:@12];//ignore相當于判斷一下
    
      //3.訂閱信號
      [ignoreSignal subscribeNext:^(id x) {
      
      //5.打印果略后的數據
         NSLog(@"%@",x);
      }];
    
      //4.發送數據
      [subject sendNext:@"1"];
    
    • 1.6.3.take:從開始一共取N次的信號


      take:從開始一共取N次的信號
       // 1、創建信號
       RACSubject *signal = [RACSubject subject];
      
       // 2、處理信號,訂閱信號
       [[signal take:2] subscribeNext:^(id x) {
      
              NSLog(@"%@",x);
       }];
      
       // 3.發送信號
       [signal sendNext:@1];
      
       [signal sendNext:@2];
      
       [signal sendNext:@3];
      
    • 1.6.4.takeLast:取最后N次的信號,前提條件,訂閱者必須調用完成,因為只有完成,就知道總共有多少信號

      takeLast:取最后N次的信號

      // 1、創建信號
      RACSubject *signal = [RACSubject subject];
      
      // 2、處理信號,訂閱信號
      [[signal takeLast:2] subscribeNext:^(id x) {
      
            NSLog(@"%@",x);
      }];
      
      // 3.發送信號
      [signal sendNext:@1];
      
      [signal sendNext:@2];
      
      [signal sendNext:@3];
      
      // 必須設置發送完成
      [signal sendCompleted];
      
    • 1.6.5.takeUntil:(RACSignal *):獲取信號直到執行完這個信號,只要傳入信號發送完成后或者發送任意數據,就不能再接受源信號的內容

      • 監聽文本框的改變,知道當前對象被銷毀

         [_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];
        

    信號處理


    takeUntil
      // takeUntil: 只要傳入信號發送完成后或者發送任意數據,就不能再接受源信號的內容
    
      // 1、創建信號
       RACSubject *signal = [RACSubject subject];
       RACSubject *subject = [RACSubject subject];
    
      // 2、處理信號,訂閱信號
      [[signal takeUntil:subject] subscribeNext:^(id x) {
      
             NSLog(@"%@",x);
       }];
    
       // 3.發送信號
       [signal sendNext:@1];
    
       [signal sendNext:@2];
    
      // [subject sendCompleted];
         [subject sendNext:@"hello"];
    
       [signal sendNext:@3];
    
    • 1.6.6.distinctUntilChanged:當上一次的值和當前的值有明顯的變化就會發出信號,否則會被忽略掉

       // 過濾,當上一次和當前的值不一樣,就會發出內容。
       // 在開發中,刷新UI經常使用,只有兩次數據不一樣才需要刷新
       [[_textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {
      
             NSLog(@"%@",x);
       }];
      
    distinctUntilChanged

    // 創建信號
    RACSubject *subject = [RACSubject subject];
    // 訂閱信號
    [[subject distinctUntilChanged] subscribeNext:^(id x) {

            NSLog(@"%@",x);
      
       }];
    
      [subject sendNext:@"1"];
      [subject sendNext:@"2"];
      [subject sendNext:@"2"];
      [subject sendNext:@"1"];
    
    • 1.6.7.skip:(NSUInteger):跳過幾個信號,不接受
      skip:(NSUInteger):跳過幾個信號,不接受
  • 1.6.8.switchToLatest:用于signalOfSignals(信號的信號),有時候信號也會發出信號,會在signalOfSignals中,獲取signalOfSignals發送的最新信號。

     RACSubject *signalOfSignals = [RACSubject subject];
     RACSubject *signal = [RACSubject subject];
     [signalOfSignals sendNext:signal];
     [signal sendNext:@1];
    
     // 獲取信號中信號最近發出信號,訂閱最近發出的信號。
     // 注意switchToLatest:只能用于信號中的信號
         [signalOfSignals.switchToLatest subscribeNext:^(id x) {
    
           NSLog(@"%@",x);
        }];
    
  • 1.7 ReactiveCocoa操作方法之秩序

    • doNext: 執行Next之前,會先執行這個Block

    • doCompleted: 執行sendCompleted之前,會先執行這個Block

      [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
             [subscriber sendNext:@1];
             [subscriber sendCompleted];
              return nil;
        }] doNext:^(id x) {
         // 執行[subscriber sendNext:@1];之前會調用這個Block
         NSLog(@"doNext");;
      }] doCompleted:^{
         // 執行[subscriber sendCompleted];之前會調用這個Block
         NSLog(@"doCompleted");;
      
      }] subscribeNext:^(id x) {
      
         NSLog(@"%@",x);
      }];
      
  • 1.8 ReactiveCocoa操作方法之線程

    • deliverOn: 內容傳遞切換到制定線程中,副作用在原來線程中,把在創建信號時block中的代碼稱之為副作用。

    • subscribeOn: 內容傳遞和副作用都會切換到制定線程中。

  • 1.9 ReactiveCocoa操作方法之時間

    • 1.9.1.timeout:超時,可以讓一個信號在一定的時間后,自動報錯

      RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
          return nil;
      }] timeout:1 onScheduler:[RACScheduler currentScheduler]];
      
      [signal subscribeNext:^(id x) {
      
             NSLog(@"%@",x);
       } error:^(NSError *error) {
            // 1秒后會自動調用
           NSLog(@"%@",error);
      }];
      
    timeout
    • 1.9.2.interval 定時:每隔一段時間發出信號

       [[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {
      
            NSLog(@"%@",x);
       }];
      
interval
  • 1.9.3.delay 延遲發送next

    delay 延遲發送next

     [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    
         [subscriber sendNext:@1];
         return nil;
     }] delay:3] subscribeNext:^(id x) {
    
         NSLog(@"%@",x);
     }];
    
  • 1.10. ReactiveCocoa操作方法之重復

    • 1.10.1.retry重試 :只要失敗,就會重新執行創建信號中的block,直到成功.

       __block int i = 0;
       [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      
          if (i == 10) {
              [subscriber sendNext:@1];
          }else{
              NSLog(@"接收到錯誤");
              [subscriber sendError:nil];
          }
          i++;
      
         return nil;
      
         }] retry] subscribeNext:^(id x) {
      
          NSLog(@"%@",x);
      
         } error:^(NSError *error) {
      }];
      
retry重試
  • 1.10.2.replay重放:當一個信號被多次訂閱,反復播放內容
replay重放

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

         [subscriber sendNext:@1];
         [subscriber sendNext:@2];
    
         return nil;
     }] replay];

     [signal subscribeNext:^(id x) {
    
          NSLog(@"第一個訂閱者%@",x);
    
     }];

    [signal subscribeNext:^(id x) {
    
         NSLog(@"第二個訂閱者%@",x);
    
     }];
  • 1.10.3.throttle節流:當某個信號發送比較頻繁時,可以使用節流,在某一段時間不發送信號內容,過了一段時間獲取信號的最新內容發出

    throttle節流

      RACSubject *signal = [RACSubject subject];
    
     // 節流,在一定時間(1秒)內,不接收任何信號內容,過了這個時間(1秒)獲取最后發送的信號內容發出。
     [[signal throttle:5] subscribeNext:^(id x) {
    
            NSLog(@"%@",x);
     }];
    
     [signal sendNext:@"我是在5s之后打印的"];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容