簡介
ReactiveCocoa 是一個重型的 FRP (Functional Reactive Programming 是一種響應變化的編程范式) 框架。內部使用了大量的block。FRP的核心就是信號。
RACSignal就是信號,是ReactiveCocoa中很重要的一個概念。RACSignal本體是RACStream。信號就是數據流,可以用來傳遞和綁定。
以下代碼基于V2.5的ReactiveCocoa
創建RACsignal
不說廢話,先來一張圖
// 源碼
+ (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;
}
源碼中創建了一個RACSubscriber來存儲nextBlock、error、completed。然后訂閱處理subscription。
這里有一個
RACCompoundDisposable
,這是一個RACDisposable
,只不過RACCompoundDisposable
可以存放多個RACDisposable
。當RACCompoundDisposable 執行dispose方法時,它所存放的disposable都會被釋放。使用
RACPassthroughSubscriber
將當前的訂閱者進行轉化,轉化為另外一種形式的訂閱者。這個訂閱者中存儲了當前的訂閱者,信號、disposable。存儲了一個信號的完整處理,并且這個訂閱者同樣遵循<RACSubscriber>協議。這里可以把RACPassthroughSubscriber當成是訂閱者的裝飾器(偽裝器)。-
使用RACPassthroughSubscriber的目的是將subscirber傳遞給另一個還沒有disposed的subscriber。
Passes through all events to another subscriber while not disposed.
-
當執行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);
}];
- 調用createSignal:創建一個信號。存儲當前的block到didSubscribe這個block中。
- 調用subscribeNext:訂閱信號。創建一個subscriber來subscription。在subscriber中存儲nextBlock,errorBlock,completeBlock三個block。
- 當前的訂閱通過轉換,成為RACPassthroughSubscriber,這個subscriber中有三個重要的屬性分別是當前訂閱的subscriber,當前的signal和RACCompoundDisposable。
- RACDynamicSignal執行
didSubscribe(RACPassthroughSubscriber)
這個block。執行RACPassthroughSubscriber中的sendNext:, sendError:, sendCompeleted
。 - RACPassthroughSubscriber中通過判斷當前的disposable的狀態來判斷是否告訴subscriber調用相應的
sendNext:
等。 - 調用dispose方法,完成整個過程。
用一張圖來來表示整個過程
引用
美團點評技術團隊RACSignal的Subscription深入分析
寫在最后
第一篇ReactiveCocoa的文章,寫的不好,如有寫的不對的地方,歡迎指正,共同進步。謝謝!!!