在介紹RACMulticastConnection之前,先說明一個Reactive Cocoa中其他幾個用到的類。
RACReplaySubject
RACSubject
RACSubject繼承RACSignal,同時也實現了RACSubscriber協議,可以看成是一個可以手動控制發送next、completed和error的信號。
RACReplaySubject繼承了RACSubject,它會保存已發送的值的(固定上限數量),每次有新的訂閱者,它都會將這些值重新發送給這些新的訂閱者,對于error和completed也是同樣的。RACReplaySubject重新實現了subscriber協議,使得這個類有了一些不同的行為。
它有一個valuesReceived屬性來存儲接收到的值,每次有新的訂閱者時,便將valuesReceived數組中的所有值發送給訂閱者。
RACMulticastConnection
Multicast意為“多播”,multicast connection的意圖是將一次對信號的訂閱分享給多個訂閱者。常見的使用情形就是:涉及的信號包含了side effect或者不希望它被多次調用。
RACMulticastConnection對外暴露的接口和屬性只有三個:
- signal 表示這個多播信號
-
- (RACDisposable *)connect
,通過訂閱signal來與它進行連接。只有調用了connect方法,信號才被訂閱,在此之前signal不回接收任何值 -
- (RACSignal *)autoconnect
,當返回的signal被訂閱時,立刻connect多播信號,當返回的信號沒有訂閱者時,自動清除多播信號的訂閱。
注意,不要主動創建RACMulticastConnection。使用RACSignal的publish或multicast方法來創建。
RACMulticastConnection的初始化方法是私有的,其實現方式如下:
- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {
NSCParameterAssert(source != nil);
NSCParameterAssert(subject != nil);
self = [super init];
if (self == nil) return nil;
_sourceSignal = source;//原信號
_serialDisposable = [[RACSerialDisposable alloc] init];
_signal = subject;//對外暴露的多播信號
return self;
}
關于connect的源代碼其實也很簡單:
- (RACDisposable *)connect {
BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);
if (shouldConnect) {
self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];//關鍵
}
return self.serialDisposable;
}
其實它主要做的就是讓對外暴露的多播信號訂閱原信號。RACMulticastConnection只是提供了實現多播的原型功能,我們真正使用到的還是RACSignal定義的相關的多播操作。
- (RACMulticastConnection *)publish {
RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
RACMulticastConnection *connection = [self multicast:subject];
return connection;
}
- (RACMulticastConnection *)multicast:(RACSubject *)subject {
[subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
return connection;
}
publish和multicast都會創建返回一個多播連接,將原來信號轉換成一個RACSubject信號.本質上,RACMulticastConnection只是為普通RACSignal與RACSubject、RACReplaySubject架起一座橋梁。
那么,多播的特性:多個訂閱者共享一個訂閱行為是如何實現的呢?在RACDynamicSignal的subscriber協議中,對subscribe的實現有這么一段:
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
可以看出,每次訂閱發生時,都會執行一次訂閱的block。而RACSubject的subscribe協議方法的實現如下:
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
NSMutableArray *subscribers = self.subscribers;
@synchronized (subscribers) {
[subscribers addObject:subscriber];
}
return [RACDisposable disposableWithBlock:^{
@synchronized (subscribers) {
// Since newer subscribers are generally shorter-lived, search
// starting from the end of the list.
NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
return obj == subscriber;
}];
if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
}
}];
}
它只是將subscriber添加到了隊列當中,并沒有再執行訂閱的block。所以本質,仍是有多個訂閱者,但是訂閱的副作用只發生一次,所以表現出共享訂閱者的行為。