ReactiveCocoa之ReactiveObjC快速入門

簡介

ReactiveCocoa(RAC)RAC是一套基于CocoaFRP(Functional Reactive Programming:函數(shù)響應(yīng)式編程)框架。

安裝:可通過CocoaPods: pod "ReactiveObjC"

思想

學(xué)習(xí)一種新的編程范式,困難不在于一門編程語言,而是怎么學(xué)會用另一種方式去思考。思路比語法更重要。

FRP(Functional Reactive Programming)函數(shù)響應(yīng)式編程,這是RAC的核心思想。FRP是一種編程范式,是組合了函數(shù)式編程和響應(yīng)式編程范式。接下來熟悉一下這兩個編程范式。

函數(shù)式編程

如何理解?

如果說面向?qū)ο笫菍?shù)據(jù)的抽象,那么函數(shù)式編程就是對行為的抽象。

就是允許把函數(shù)本身作為參數(shù)傳入另一個函數(shù),還允許返回一個函數(shù)!它的核心是函數(shù)。在解決問題時,使用不可變值和函數(shù),函數(shù)對一個值進行處理,映射成另一個值。

關(guān)鍵點: 參數(shù)是個函數(shù) 返回值也是函數(shù)

現(xiàn)實生活中行為:

洗車:可以將函數(shù)比作一個自動洗車間,臟的汽車進去,自動洗完后,出來還是汽車,只是汽車變干凈了。

響應(yīng)式編程

如何理解?

面向數(shù)據(jù)流的編程思想,類似觀察者模式一樣的表現(xiàn)。

計算是前后呼應(yīng)的,相互關(guān)聯(lián),有些變化了,彼此之間的關(guān)系也會隨著值的變化而變化。

現(xiàn)實生活中行為:

防火:室內(nèi)煙霧散發(fā)到一定程度時,煙感探測器探測到安全極限,及時發(fā)出失火警報。

關(guān)鍵點: 相互關(guān)聯(lián)、跟隨變化

開始學(xué)習(xí)RAC

RAC中Signal的基本的訂閱過程

  1. 創(chuàng)建信號

  2. 訂閱信號

  3. 發(fā)送信號

  4. 取消信號

RAC常用的類

RACSignal

RACSignal(冷信號)繼承于抽象類RACStream,是RAC的核心類,信號只有被訂閱時才會送出信號值。

冷信號特點:

  • 不可變;
  • 被動;

創(chuàng)建冷信號

RACSignal.h中的聲明的createSignal類方法如下:

+ (RACSignal<ValueType> *)createSignal:(RACDisposable * _Nullable (^)(id<RACSubscriber> subscriber))didSubscribe RAC_WARN_UNUSED_RESULT;

createSignal 類方法說明:

  • 作用:創(chuàng)建新的信號
  • 參數(shù):didSubscribe,是一個block類型變量,didSubscribe block變量自身是一個類似C語言的函數(shù)指針變量,didSubscribe 指向的函數(shù)包含一個RACSubscriber 參數(shù),返回一個RACDisposable 對象。
  • 返回值:返回一個RACSignal類型的信號。

例:

 //創(chuàng)建信號
  RACSignal *singalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    //發(fā)送信號
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@(2)];
        [subscriber sendCompleted];
    
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"------this is RACDisposable---------");
        }];
    }];
    //訂閱信號
    [singalA subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------singalA value is %@-------",x);
    }];

返回結(jié)果如下:
2021-04-10 22:10:30.127793+0800 RACDemo[14148:238689] -------singalA value is 1-------
2021-04-10 22:10:30.127938+0800 RACDemo[14148:238689] -------singalA value is 2-------
2021-04-10 22:10:30.128098+0800 RACDemo[14148:238689] ------this is RACDisposable---------

上面的過程拆分一下可以這樣寫:

 //聲明一名稱為didSubscribe的block變量,帶有一個RACSubscriber類型參數(shù),返回一個RACDisposable類型對象
    RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
    //為didSubscribe定義,(block的實現(xiàn))
    didSubscribe = ^RACDisposable * (id<RACSubscriber> sub){
      //發(fā)送信號
        [sub sendNext:@"1"];
        [sub sendNext:@(2)];
        [sub sendCompleted];
        [sub sendError:nil];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"----didSubscribe--RACDisposable--------");
        }];
    };
    //創(chuàng)建信號
    RACSignal *signalB = [RACSignal createSignal:didSubscribe];
    //訂閱信號
    [signalB subscribeNext:^(id  _Nullable x) {
        NSLog(@"------signalB x is %@--------",x);
    }];
返回結(jié)果如下:
2021-04-10 22:13:58.758873+0800 RACDemo[14258:241973] ------signalB x is 1--------
2021-04-10 22:13:58.758993+0800 RACDemo[14258:241973] ------signalB x is 2--------
2021-04-10 22:13:58.759078+0800 RACDemo[14258:241973] ----didSubscribe--RACDisposable-------

RACSubscriber

訂閱者:負責發(fā)信號,本身是個協(xié)議;只要遵循它,并且實現(xiàn)協(xié)議內(nèi)的方法,就會成為訂閱者。

常用的幾個方法:

//發(fā)送值到訂閱者
- (void)sendNext:(nullable id)value;
//發(fā)送錯誤給訂閱者
- (void)sendError:(nullable NSError *)error;
//告訴訂閱者發(fā)送完成了
- (void)sendCompleted;

RACDisposable

取消訂閱或清理資源,發(fā)送失敗或者完成,會自動觸發(fā)取消訂閱方法。

//
+ (instancetype)disposableWithBlock:(void (^)(void))block;
//取消訂閱
- (void)dispose;

RACSubject

RACSubject(熱信號)t繼承于RACSignal,一個subject可認為是一個信號,你可以手動控制sendNext、sendError、sendCompleted事件??梢詭椭銓⒎荝AC橋接到RAC上。

熱信號特點

  • 可變;

  • 主動;

  • 可以橋接RAC和非RAC;

RACSubject 對外公開的接口只有兩個,一個是創(chuàng)建一個熱信號對象,另一個是發(fā)送消息的方法。

/// Returns a new subject.
+ (instancetype)subject;

// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value;

創(chuàng)建熱信號的接口:

+ (instancetype)subject;

這個方法創(chuàng)建的時候,會初始化兩個私有屬性,一個是subscribers數(shù)組 、另一個是disposable。

subscribers:數(shù)組里包含了所有的當前訂閱者,在self的同步操作時候會使用它遍歷所屬的訂閱者。

disposable:包含接收者對其他信號的所有訂閱。主要是用于取消訂閱和清理資源的操作。

發(fā)送信號的接口:

- (void)sendNext:(nullable ValueType)value;

重新定義了RACSubscriber協(xié)議的sendNext方法。

創(chuàng)建熱信號

例:創(chuàng)建一個熱信號

  //創(chuàng)建信號
    RACSubject *subject=[RACSubject subject];
    //訂閱信號
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"subject x is [%@]",x);
    }];
    //發(fā)送信號
    [subject sendNext:@"123"];
    //結(jié)束訂閱
    [subject sendCompleted];
返回結(jié)果如下:
  2021-04-13 16:48:52.043076+0800 RACDemo[29017:264320] subject x is [123]

常用高階函數(shù)

綁定 bind

作用: 可將信號攔截后進行過濾再發(fā)送給新的信號,類似消息轉(zhuǎn)發(fā)

typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);
- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block RAC_WARN_UNUSED_RESULT;

例:

  //    //創(chuàng)建信號
     RACSubject *subject = [RACSubject subject];
    //綁定新的信號
    RACSignal *singnal=[subject bind:^RACSignalBindBlock _Nonnull {
        return ^RACSignal*(id value,BOOL *stop){
            return[RACReturnSignal return:value];
        };
    }];
    // 訂閱信號
    [singnal subscribeNext:^(id  _Nullable x) {
        NSLog(@"bind x is [%@]",x);
    }];
    //發(fā)送信號
    [subject sendNext:@"send bind value 123"];
返回結(jié)果如下:
2021-04-12 10:16:57.562421+0800 RACDemo[4075:59403] bind x is [send bind value 123]

合并

concat

作用:按順序合并多個信號為一個信號,其中前一個信號必須發(fā)送sendCompleted表示執(zhí)行完成,才會執(zhí)行后面一個信號,類似串行隊列,先完成一個請求,才進行下一個請求。

- (RACSignal *)concat:(RACSignal *)signal RAC_WARN_UNUSED_RESULT;

例:

 //創(chuàng)建信號
    RACSubject *subject=[RACSubject subject];
    //創(chuàng)建信號
    RACSubject *subject2=[RACSubject subject];
    //連接信號
    RACSignal *signal=[subject concat:subject2];
    //訂閱信號
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"concat x is [%@]",x);
    }];
    //發(fā)送信號
    [subject sendNext:@"abc"];
    //subject 信號執(zhí)行完成,必須加上 sendCompleted,表示信號結(jié)束了,否則無法連接下一個信號
    [subject sendCompleted];
    //subject2發(fā)送信號
    [subject2 sendNext:@"ABC"];
返回結(jié)果如下:
2021-04-12 16:42:14.281303+0800 RACDemo[17888:262262] concat x is [abc]
2021-04-12 16:42:14.281532+0800 RACDemo[17888:262262] concat x is [ABC]

merge

作用:按順序合并多個信號為一個信號,其中前一個信號不添加sendCompleted,也會執(zhí)行后面一個信號。只要有一個信號來,合并操作就會接受。不強調(diào)先完成一個再進行下一個。

例:

      //創(chuàng)建信號
      RACSubject *subject=[RACSubject subject];
      //創(chuàng)建信號
      RACSubject *subject2=[RACSubject subject];
      //連接信號
      RACSignal *signal=[subject merge:subject2];
      //訂閱信號
      [signal subscribeNext:^(id  _Nullable x) {
          NSLog(@"merge x is [%@]",x);
      }];
      //發(fā)送信號
      [subject sendNext:@"abc"];
      //subject2發(fā)送信號
      [subject2 sendNext:@"ABC"];
返回結(jié)果如下:
2021-04-12 17:08:23.410822+0800 RACDemo[19726:287122] merge x is [abc]
2021-04-12 17:08:23.411005+0800 RACDemo[19726:287122] merge x is [ABC]

combineLatest

作用:按順序合并多個信號的最新值為元組

例:

 //創(chuàng)建信號
    RACSubject *subject = [RACSubject subject];
    //創(chuàng)建信號
    RACSubject *subject2 = [RACSubject subject];
    RACSignal *signal = [RACSignal combineLatest: @[subject, subject2]];
    //訂閱信號
       [signal subscribeNext:^(id  _Nullable x) {
           NSLog(@"combineLatest x is  [%@]",x);
       }];
    //發(fā)送信號
    [subject sendNext:@"a"];
    //發(fā)送信號
    [subject2 sendNext:@"b"];
    //發(fā)送信號
    [subject2 sendNext:@"A"];
   //發(fā)送信號
    [subject sendNext:@"B"];
返回結(jié)果如下:
  2021-04-12 17:27:35.030304+0800 RACDemo[20891:303336] combineLatest x is  [<RACTuple: 0x600003af40b0> (
    a,
    b
)]
2021-04-12 17:27:35.030604+0800 RACDemo[20891:303336] combineLatest x is  [<RACTuple: 0x600003ae4ee0> (
    a,
    A
)]
2021-04-12 17:27:35.030805+0800 RACDemo[20891:303336] combineLatest x is  [<RACTuple: 0x600003ae85c0> (
    B,
    A
)]

映射

flattenMap

作用: 用于信號中信號,把源信號的內(nèi)容映射成一個新的信號,信號是RACSignal類型

- (RACSignal *)flattenMap:(__kindof RACSignal * _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;

例:映射值后,返回一下新的信號

//    //創(chuàng)建信號
    RACSubject *subject = [RACSubject subject];
    // 綁定信號,并返回映射后的值
    RACSignal * flattenMapSignal =[subject flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
        value=[NSString stringWithFormat:@"flattenMapSignal映射后的值為:%@",value];
        return [RACReturnSignal return:value];
    }];
    //訂閱信號
    [flattenMapSignal subscribeNext:^(id  _Nullable x) {
         NSLog(@"flattenMapSignal x is [%@]",x);
      }];
    //發(fā)送信號
    [subject sendNext:@"1234"];
返回結(jié)果如下:
2021-04-11 08:30:20.425283+0800 RACDemo[5943:81326] flattenMapSignal x is [flattenMapSignal映射后的值為:1234]

map

作用: 把源信號的值映射成一個新的值,可以是任意類型的

- (RACSignal *)map:(id _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;

例:返回加工后的字符串

   //創(chuàng)建信號
        RACSubject *subject = [RACSubject subject];
        //綁定信號
    RACSignal *bindSignal = [subject map:^id _Nullable(id  _Nullable value) {
        //信號發(fā)送的內(nèi)容
        NSString *str = [NSString stringWithFormat:@"映射 map 處理后的數(shù)據(jù):%@", value];
        //返回任意類型的值
            return str;
        }];
        //訂閱信號
        [bindSignal subscribeNext:^(id x) {
        NSLog(@"map x is [%@]",x);
        }];
        //發(fā)送數(shù)據(jù)
        [subject sendNext:@1];
返回結(jié)果如下:
  2021-04-11 08:34:34.676217+0800 RACDemo[6105:85742] map x is [映射 map 處理后的數(shù)據(jù):1]

例:返回一個新的信號

 RACSubject *subject2=[RACSubject subject];
    //綁定信號
   RACSignal * bindSignal2=[subject2 map:^RACSignal * _Nullable(id  _Nullable value) {
       //映射后,返回一個新的信號對象
       value=[NSString stringWithFormat:@"修改value %@",value];
      return [RACReturnSignal return:value];
    }];
    //訂閱信號
    [bindSignal2 subscribeNext:^(id  _Nullable x) {
        NSLog(@"返回值是信號  [%@]",x);
    }];
    //發(fā)送信號
    [subject2 sendNext:@(2)];
返回結(jié)果如下:
2021-04-11 08:41:03.927420+0800 RACDemo[6274:90310] 返回值是信號  [<RACReturnSignal: 0x600001e9d400> name: ]
    

過濾

filter

作用: 通過條件判斷,過濾掉相應(yīng)的信號

- (RACSignal<ValueType> *)filter:(BOOL (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;

例:過濾掉ABC開頭的字符串

 //創(chuàng)建信號
    RACSubject *subject=[RACSubject subject];
    //過濾掉ABC開頭的字符串
    RACSignal *signal= [subject filter:^BOOL(id  _Nullable value) {
        NSString *str=value;
        if ([str hasPrefix:@"ABC"]) {
            return NO;
        }else{
            return YES;
        }
    }];
    //訂閱信號
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"過濾后的結(jié)果為 [%@]",x);
    }];
    //發(fā)送信號
    [subject sendNext:@"abc123"];
    //發(fā)送信號
    [subject sendNext:@"ABC123"];
返回結(jié)果如下:
2021-04-11 10:44:03.423107+0800 RACDemo[10384:185744] 過濾后的結(jié)果為 [abc123]

ignore

作用: 過濾信號值,忽略掉值為xxx的信號,按值忽略

- (RACSignal<ValueType> *)ignore:(nullable ValueType)value RAC_WARN_UNUSED_RESULT;

例:忽略掉值為ABC123的信號

 //創(chuàng)建信號
    RACSubject* subject=[RACSubject subject];
    //忽略掉值為ABC123的信號
    RACSignal *signal=[subject ignore:@"ABC123"];
    //訂閱信號
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"ignore x is [%@]",x);
    }];
    //發(fā)送信號
    [subject sendNext:@"ABC123"];
    //發(fā)送信號
    [subject sendNext:@"abc123"];
返回結(jié)果如下:
2021-04-11 10:37:35.490486+0800 RACDemo[10205:180228] ignore x is [abc123]

then

- (RACSignal *)then:(RACSignal * (^)(void))block RAC_WARN_UNUSED_RESULT;

作用:忽略掉前一個信號發(fā)送的值,必須等前一個信號發(fā)送sendCompleted表示執(zhí)行完成后,才執(zhí)行下一個信號,否則無效,按信號忽略

例:忽略掉subject 信號,然后再執(zhí)行subject2信號

    //創(chuàng)建信號
    RACSubject *subject = [RACSubject subject];
    //創(chuàng)建信號
    RACSubject *subject2 = [RACSubject subject];
    //忽略掉subject 信號
    RACSignal *signal = [subject then:^RACSignal * _Nonnull{
        return subject2;
    }];
    //訂閱信號
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"then x is  [%@]",x);
    }];
    
    //發(fā)送信號
    [subject sendNext:@"a"];
    // 必須添加sendCompleted ,表示subject 信號執(zhí)行完成,否則無效
    [subject sendCompleted];
    //發(fā)送信號
    [subject2 sendNext:@"b"];
    //發(fā)送信號
    [subject2 sendNext:@"A"];
返回結(jié)果如下:
2021-04-12 17:45:59.354229+0800 RACDemo[22106:321645] then x is  [b]
2021-04-12 17:45:59.354343+0800 RACDemo[22106:321645] then x is  [A]

take

作用: 按順序獲取第1次到n次信號

- (RACSignal<ValueType> *)take:(NSUInteger)count RAC_WARN_UNUSED_RESULT;

例:獲取從第1~4次信號

    //創(chuàng)建信號
    RACSubject* subject=[RACSubject subject];
    //獲取從第1~4次信號,若傳0則不取信號
    RACSignal *signal=[subject take:4];
    //訂閱信號
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"take x is [%@]",x);
    }];
    //發(fā)送信號
    [subject sendNext:@"ABC123"];
    //發(fā)送信號
    [subject sendNext:@"123ABC"];
    //發(fā)送信號
    [subject sendNext:@"abc123"];
    //發(fā)送信號
    [subject sendNext:@"123abc"];
    //發(fā)送信號
    [subject sendNext:@"123"];
    //發(fā)送信號
    [subject sendNext:@"abc"];
返回結(jié)果如下:
2021-04-11 10:59:05.655107+0800 RACDemo[10795:197326] take x is [ABC123]
2021-04-11 10:59:05.655298+0800 RACDemo[10795:197326] take x is [123ABC]
2021-04-11 10:59:05.655436+0800 RACDemo[10795:197326] take x is [abc123]
2021-04-11 10:59:05.655573+0800 RACDemo[10795:197326] take x is [123abc]

skip

作用: 跳過從第1次~n次 的信號

例:跳過從第1~3次信號

 //創(chuàng)建信號
    RACSubject* subject=[RACSubject subject];
    //跳過從第1~3次信號
    RACSignal *signal=[subject skip:3];
    //訂閱信號
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"skip x is [%@]",x);
    }];
    //發(fā)送信號
    [subject sendNext:@"123"];
    //發(fā)送信號
    [subject sendNext:@"abc"];
    //發(fā)送信號
    [subject sendNext:@"ABC"];
    //發(fā)送信號
    [subject sendNext:@"123abc"];
返回結(jié)果如下:
2021-04-11 11:18:20.866481+0800 RACDemo[11294:209783] skip x is [123abc]

switchToLatest

作用: 信號內(nèi)的信號,獲取信號內(nèi)信號發(fā)送的最新的信號,當發(fā)送多個內(nèi)部信號時候,只接受最新的內(nèi)部信號

例:


    //創(chuàng)建信號
    RACSubject* subject=[RACSubject subject];
    //創(chuàng)建內(nèi)部信號,用于subject內(nèi)部的信號
    RACSubject * innerSubject=[RACSubject subject];
    //創(chuàng)建內(nèi)部信號1,用于subject內(nèi)部的信號
      RACSubject * innerSubject1=[RACSubject subject];
    //訂閱信號,訂閱subject內(nèi)的即innerSubject信號
    [subject.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"switchToLatest x is [%@]",x);
    }];
    //subject發(fā)送innerSubject信號
    [subject sendNext:innerSubject];
    //subject發(fā)送innerSubject1信號
    [subject sendNext:innerSubject1];
    //通過innerSubject來發(fā)送信號
    [innerSubject sendNext:@"abc"];
   //通過innerSubject1來發(fā)送信號
    [innerSubject1 sendNext:@"123abc"];
返回結(jié)果如下:
2021-04-11 11:41:27.553097+0800 RACDemo[12001:229779] switchToLatest x is [123abc]

distinctUntilChanged

作用: 忽略重復(fù)的信號

例:執(zhí)行后,忽略了重復(fù)的1

 //創(chuàng)建信號
    RACSubject* subject=[RACSubject subject];
    //訂閱信號,只在訂閱的信號與上一次不同時才會響應(yīng)
    [subject.distinctUntilChanged subscribeNext:^(id  _Nullable x) {
        NSLog(@"distinctUntilChanged x is [%@]",x);
    }];
     //發(fā)送信號
    [subject sendNext:@"1"];
     //發(fā)送信號
    [subject sendNext:@"1"];
     //發(fā)送信號
     [subject sendNext:@"123"];
返回結(jié)果如下:
2021-04-11 11:53:14.833259+0800 RACDemo[12347:240090] distinctUntilChanged x is [1]
2021-04-11 11:53:14.833524+0800 RACDemo[12347:240090] distinctUntilChanged x is [123]

總結(jié)

RAC的高內(nèi)聚,低耦合特性,很好的配合了MVVM的使用。因此如果現(xiàn)在正在使用MVVM模式開發(fā),不妨試一下RAC框架。 以上是對RAC中的常用類、函數(shù)、宏的一些使用說明,并未對每個功能的底層原理進行分析,主要是介紹RAC是如何應(yīng)用于業(yè)務(wù)中的。學(xué)習(xí)一項新的框架的步驟,一般是先知其所用,而后再知其所以然,后續(xù)再對某些功能進行詳細的分析。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容