ReactiveCocoa信號發送詳解

簡介

ReactiveCocoa 是一個重型的 FRP (Functional Reactive Programming 是一種響應變化的編程范式) 框架。內部使用了大量的block。FRP的核心就是信號。
RACSignal就是信號,是ReactiveCocoa中很重要的一個概念。RACSignal本體是RACStream。信號就是數據流,可以用來傳遞和綁定。

以下代碼基于V2.5的ReactiveCocoa

創建RACsignal

不說廢話,先來一張圖

RACSignal.png
// 源碼
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

通過RACDynamicSignal創建信號,此時傳入一個block,這個block的參數是一個遵循RACSubscriber協議的一個變量,同時這個block的返回值是一個RACDisposable類型。

通過源碼分析,看到RACDynamicSignal有一個屬性didSubscribe存儲了傳進來的的block,這個屬性將在之后訂閱的時候使用。

這個RACSubscriber的協議,其中定義了幾個方法

@protocol RACSubscriber <NSObject>
@required

// 發送next需要執行的參數
- (void)sendNext:(id)value;
// 發送錯誤
- (void)sendError:(NSError *)error;
// 發送成功
- (void)sendCompleted;
// 處理信號,是否釋放取消訂閱。
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;

@end

創建一個信號

RACSignal *aSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [subscriber sendNext:@"a"];
    [subscriber sendCompleted];
    return [RACDisposable disposableWithBlock:^{
        
    }];
}];

訂閱

一個信號通過調用subscribeNext創建一個subscriber進行subscription。

// RACSignal (Subscription) RACSignal.m
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

// 當前self是RACDynamicSignal所以,使用RACDynamicSignal.m中的subscribe:方法。
// RACDynamicSignal.m
- (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);
            [disposable addDisposable:innerDisposable];
        }];

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}
  1. 源碼中創建了一個RACSubscriber來存儲nextBlock、error、completed。然后訂閱處理subscription。

  2. 這里有一個RACCompoundDisposable,這是一個RACDisposable,只不過RACCompoundDisposable可以存放多個RACDisposable 。當RACCompoundDisposable 執行dispose方法時,它所存放的disposable都會被釋放。

  3. 使用RACPassthroughSubscriber將當前的訂閱者進行轉化,轉化為另外一種形式的訂閱者。這個訂閱者中存儲了當前的訂閱者,信號、disposable。存儲了一個信號的完整處理,并且這個訂閱者同樣遵循<RACSubscriber>協議。這里可以把RACPassthroughSubscriber當成是訂閱者的裝飾器(偽裝器)。

  4. 使用RACPassthroughSubscriber的目的是將subscirber傳遞給另一個還沒有disposed的subscriber。

    Passes through all events to another subscriber while not disposed.

  5. 當執行self.didSubscribe(subscriber)時siganle存儲的block就會被執行。當sendNext:執行時,先執行[RACPassthroughSubscriber sendNext:],然后調用RACPassthroughSubscriber中的subscriber來執行sendNext:

    // 源碼
        // 源碼
    - (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];
    

}
繼續執行addDisposable,此時會將RACCompoundDisposable```釋放。

```objc
// 源碼
- (void)addDisposable:(RACDisposable *)disposable {
NSCParameterAssert(disposable != self);
if (disposable == nil || disposable.disposed) return;

BOOL shouldDispose = NO;

OSSpinLockLock(&_spinLock);
{
    if (_disposed) {
        shouldDispose = YES;
    } else {
        #if RACCompoundDisposableInlineCount
        for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
            if (_inlineDisposables[i] == nil) {
                _inlineDisposables[i] = disposable;
                goto foundSlot;
            }
        }
        #endif

        if (_disposables == NULL) _disposables = RACCreateDisposablesArray();
        CFArrayAppendValue(_disposables, (__bridge void *)disposable);

        if (RACCOMPOUNDDISPOSABLE_ADDED_ENABLED()) {
            RACCOMPOUNDDISPOSABLE_ADDED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount);
        }

    #if RACCompoundDisposableInlineCount
    foundSlot:;
    #endif
    }
}
OSSpinLockUnlock(&_spinLock);

// Performed outside of the lock in case the compound disposable is used
// recursively.
// 會在此處釋放。
if (shouldDispose) [disposable dispose];

}


6. 源碼中可以看出,訂閱一個信號,返回的是一個RACDisposable,作為一個返回值返回到外部,我們可以在外部對其取消這個訂閱。

###總結
```objc
// part 1.
RACSignal *aSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    // 此處subscriber為轉換后的subscriber
    // part 5.
   [subscriber sendNext:@"abc"];
   [subscriber sendCompleted];
    
   // part 6.
   return [RACDisposable disposableWithBlock:^{
       NSLog(@"disposable");
   }];
}];

// part 2.
RACDisposable *adisposable = [aSignal subscribeNext:^(id x) {
   NSLog(@"~~~~~~~~~~~  %@",x);
}];
  1. 調用createSignal:創建一個信號。存儲當前的block到didSubscribe這個block中。
  2. 調用subscribeNext:訂閱信號。創建一個subscriber來subscription。在subscriber中存儲nextBlock,errorBlock,completeBlock三個block。
  3. 當前的訂閱通過轉換,成為RACPassthroughSubscriber,這個subscriber中有三個重要的屬性分別是當前訂閱的subscriber,當前的signal和RACCompoundDisposable。
  4. RACDynamicSignal執行didSubscribe(RACPassthroughSubscriber)這個block。執行RACPassthroughSubscriber中的sendNext:, sendError:, sendCompeleted
  5. RACPassthroughSubscriber中通過判斷當前的disposable的狀態來判斷是否告訴subscriber調用相應的sendNext:等。
  6. 調用dispose方法,完成整個過程。

用一張圖來來表示整個過程


ReactiveCocoa.jpg

引用

ReactiveCocoa gitHub

美團點評技術團隊RACSignal的Subscription深入分析

iOS ReactiveCocoa詳解

寫在最后

第一篇ReactiveCocoa的文章,寫的不好,如有寫的不對的地方,歡迎指正,共同進步。謝謝!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容