ReactiveCocoa 使用.RACSignal(訊號源)類使用

weixin_34007291發表於2017-08-31

RACSignal 是RAC的核心,幾乎所有的操作都是圍繞著RACSignal類在執行的。這一章會講解RACSignal類的建立和使用。如果讀者對ReactiveCocoa的一些名詞和架構不熟悉可以看上一篇ReactiveCocoa 基礎.架構介紹

完成一個訊號的生命週期大體分為四步

  • 建立訊號
  • 訂閱訊號
  • 傳送訊號
  • 取消訂閱
7343197-1605f654e1062e5a.png
螢幕快照 2017-08-31 下午3.33.26.png

程式碼範例

  1 建立Signal
  RACSignal *testSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            // 3 傳送訊號
            [subscriber sendNext:@"土豆蘿蔔君"];
            [subscriber sendCompleted];
            
            return [RACDisposable disposableWithBlock:^{
                //4 用於取消訂閱清理資源 一般用來釋放一些變數和物件 如果沒有的話此處可以為nil
            }];
        }];

  2訂閱Next訊號
    [testSignal subscribeNext:^(id x) {
            NSLog(@"%@",x);
    }];

內部解析

1 建立訊號

1.1 由上面的訊號類使用圖和程式碼可知,建立訊號類方法中傳入了一個返回值是RACDisposable 型別的例項變數,引數為id型別且遵守且遵守RACSubscriber協議的subscriber訂閱者,名為didSubscribe的block 程式碼如下

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    return [RACDynamicSignal createSignal:didSubscribe];
}

1.2 類建立的是一個 RACDynamicSignal 型別的動態訊號,並將 didSubscribe 傳入

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];  //重要方法 儲存上一個傳入的didSubscribe block  
    return [signal setNameWithFormat:@"+createSignal:"];
}

建立了一個 RACDynamicSignal 型別的訊號,然後將傳入的名為 didSubscribe 的block儲存在建立的訊號的 didSubscribe 屬性中儲存但是並未觸發 (在有訂閱者訂閱此訊號時候執行,此時只是一個冷訊號)

2 訂閱訊號

訂閱訊號有subscribeNext, subscribeError, subscribeComlepted 三種方法。當訊號被 訂閱的時候此時冷訊號變成了熱訊號就可以執行傳送訊號的操作。

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);  //如果nextBlock為空不在執行
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
  //內部建立了RACSubscriber(訂閱者)類的例項物件o,並且將nextBlock儲存到o中,在返回值出執行o,實際也是執行了nextBlock。
    return [self subscribe:o];
}

2.1 上面程式碼建立訂閱者的實質是,建立一個訂閱者,並儲存相應的block 此時是儲存並未觸發![self subscribe:o] 方法才是最重要的 (RACDynamicSignal類中)

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];

    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

    if (self.didSubscribe != NULL) {
            RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{

            RACDisposable *innerDisposable = self.didSubscribe(subscriber);
             //在這裡執行了在步驟1傳入的 didSubscribe block 並返回一個RACDisposable型別的例項變數

            [disposable addDisposable:innerDisposable];
        }];

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}
  • 1 程式碼中傳入了2.1中建立的RACSubscriber *o 訂閱者,裡面儲存著 步驟1建立訊號時的didSubscribe block

  • 2 這裡生成了一個RACCompoundDisposable型別的disposable 主要用來管理訂閱結束以及資源的清理

  • 3 RACPassthroughSubscriber 型別的訂閱者主要是對傳入的訂閱者,當前的訊號以及建立的disposable 進行一個包裝,感覺作用就是做一個變數的統一入口,實際起作用的應該還是剛傳入的訂閱者

  • 4 執行 didSubscribe block 將返回過來的innerDisposable 傳入剛剛生成的disposable,也把排程器返回過來的schedulingDisposable 也儲存到disposable 然後統一管理。當 RACCompoundDisposable 物件被 disposed 時,它會呼叫其所包含的所有 disposable 物件的 -dispose 方法從而做到統一管理

  • 5 總結訂閱訊號本質就是建立了一個 RACPassthroughSubscriber 型別的訂閱者,並將傳入的程式碼塊儲存起來,留待以後呼叫,同時呼叫了第一步建立訊號中儲存的程式碼塊,並傳入建立的訂閱者

3 傳送訊號
- (void)sendNext:(id)value {
    if (self.disposable.disposed) return;

    if (RACSIGNAL_NEXT_ENABLED()) {
        RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
    }

    [self.innerSubscriber sendNext:value];
}

- (void)sendError:(NSError *)error {
    if (self.disposable.disposed) return;

    if (RACSIGNAL_ERROR_ENABLED()) {
        RACSIGNAL_ERROR(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString(error.description));
    }

    [self.innerSubscriber sendError:error];
}

- (void)sendCompleted {
    if (self.disposable.disposed) return;

    if (RACSIGNAL_COMPLETED_ENABLED()) {
        RACSIGNAL_COMPLETED(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description));
    }

    [self.innerSubscriber sendCompleted];
}
  • 傳送訊號就是執行訂閱時候傳入的next error completed的block

  • 對於 sendError 和 sendCompleted 都是先取消訂閱,再執行相應的程式碼塊,而 sendNext 並未使訂閱結束,這樣的話,對之後討論的各種組合方法中必須寫上 sendCompleted來結束訂閱的做法就好理解了也就印證了上一篇的 "一個訊號的生命週期是由任意個 next 事件和一個 error 事件或一個 completed 事件組成的"這句話。

4取消訂閱

想要結束訂閱只要將相應生成的disposable執行dispose即可。原因在於步驟2中的排程器

   RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
           RACDisposable *innerDisposable = self.didSubscribe(subscriber);
           [disposable addDisposable:innerDisposable];
       }];
  • 首先我們要知道執行訂閱的程式碼塊實際上是放到相應的排程器中去執行,接下來點選方法中
- (RACDisposable *)schedule:(void (^)(void))block {
   NSCParameterAssert(block != NULL);

   if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];

   block();
   return nil;
}

  • 如果currentScheduler不為nil的時候程式碼塊就不會放到排程器裡去執行,而是直接執行,此時disposable的dispose方法就沒有用了

  • 如果currentScheduler為空的時候會執行

- (RACDisposable *)schedule:(void (^)(void))block {
    NSCParameterAssert(block != NULL);

    RACDisposable *disposable = [[RACDisposable alloc] init];

    dispatch_async(self.queue, ^{
        if (disposable.disposed) return;
        [self performAsCurrentScheduler:block];
    });
    return disposable;
}

  • 程式碼塊未被排程的之前,生成的disposable被dispose的話,程式碼塊就不會被執行。

總結:

  • 步驟1 建立訊號本質就是建立了一個 RACDynamicSignal型別的動態訊號,並將傳入block儲存起來
  • 步驟2 訂閱訊號本質就是建立了一個 RACPassthroughSubscriber 型別的訂閱者,並將傳入的nextBlock儲存起來同時呼叫了第一步建立訊號中儲存的程式碼塊,並傳入建立的訂閱者
  • 步驟3 傳送訊號就是執行訂閱訊號時對應的block。
  • 步驟4 取消訂閱就是把訂閱訊號獲得的disposable 進行dispose即可在排程器排程該部分之前直接返回不在繼續執行達到取消的效果

相關文章