RACMulticastConnection涉及到熱信號盒揉,RACDisposable的一些概念屿岂。這里對源碼進行分析做一個透徹的理解。
美團技術(shù)博客關(guān)于冷熱信號有3篇文章:美團技術(shù)博客ReactiveCocoa冷熱信號居暖,給出的冷熱信號的區(qū)別:
1. Hot Observable是主動的,盡管你并沒有訂閱事件菱蔬,但是它會時刻推送,就像鼠標移動史侣;而Cold Observable是被動的拴泌,只有當你訂閱的時候,它才會發(fā)布消息惊橱。
2. Hot Observable可以有多個訂閱者蚪腐,是一對多,集合可以與訂閱者共享信息税朴;而Cold Observable只能一對一削茁,當有不同的訂閱者,消息是重新完整發(fā)送掉房。
這里從源碼層級深入了解一下。下面是一個使用熱信號的示例代碼一:
// 示例代碼一
// <1>創(chuàng)建RACMulticastConnection
RACMulticastConnection *connection = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
[subscriber sendNext:@1];
}];
[[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
[subscriber sendNext:@2];
}];
[[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
[subscriber sendNext:@3];
}];
[[RACScheduler mainThreadScheduler] afterDelay:4 schedule:^{
[subscriber sendCompleted];
}];
return nil;
}] publish];
// <2> RACMulticastConnection進行connect
[connection connect];
// <3> 對connection.signal進行訂閱
RACSignal *signal = connection.signal;
NSLog(@"Signal was created.");
[[RACScheduler mainThreadScheduler] afterDelay:1.1 schedule:^{
[signal subscribeNext:^(id x) {
NSLog(@"Subscriber 1 recveive: %@", x);
}];
}];
示例代碼一將冷信號通過RACMulticastConnection轉(zhuǎn)化成了熱信號慰丛。
步驟<1> 創(chuàng)建RACMulticastConnection
示例代碼一的<1>是對一個冷信號發(fā)送publish消息創(chuàng)建了一個RACMulticastConnection卓囚。RACMulticastConnection的sourceSignal屬性保存了要轉(zhuǎn)化的冷信號,signal屬性保存了一個新創(chuàng)建的RACSubject诅病,其實RACSubject就是一個熱信號哪亿。
步驟<2> RACMulticastConnection進行connect
示例代碼一的<2>是使用RACMulticastConnection的熱信號signal對冷信號sourceSignal進行訂閱,
- (RACDisposable *)connect {
BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);
if (shouldConnect) {
self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
}
return self.serialDisposable;
}
上面代碼中的[self.sourceSignal subscribe:_signal]里的subscribe調(diào)用的是RACDynamicSignal的subscribe方法贤笆,看一下這個方法的源碼:
RACDynamic.m
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
// <a>
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
OSSpinLockLock(&_subscribersLock);
if (_subscribers == nil) {
_subscribers = [NSMutableArray arrayWithObject:subscriber];
} else {
[_subscribers addObject:subscriber];
}
OSSpinLockUnlock(&_subscribersLock);
@weakify(self);
RACDisposable *defaultDisposable = [RACDisposable disposableWithBlock:^{
@strongify(self);
if (self == nil) return;
BOOL stillHasSubscribers = YES;
OSSpinLockLock(&_subscribersLock);
{
// 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];
stillHasSubscribers = _subscribers.count > 0;
}
}
OSSpinLockUnlock(&_subscribersLock);
if (!stillHasSubscribers) {
[self invalidateGlobalRefIfNoNewSubscribersShowUp];
}
}];
[disposable addDisposable:defaultDisposable];
// <b>
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
if (innerDisposable != nil) [disposable addDisposable:innerDisposable];
}];
if (schedulingDisposable != nil) [disposable addDisposable:schedulingDisposable];
}
return disposable;
}
上述代碼<a>部分依據(jù)參數(shù)subscriber蝇棉,創(chuàng)建了一個新的subscriber;參數(shù)subscriber代入的是RACMulticastConnection的RACSubject類型的signal屬性芥永,返回的subscriber是RACPassthroughSubscriber篡殷。初始化源碼如下所示:
RACPassthroughSubscriber.m
- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
NSCParameterAssert(subscriber != nil);
self = [super init];
if (self == nil) return nil;
_innerSubscriber = subscriber;
_signal = signal;
_disposable = disposable;
[self.innerSubscriber didSubscribeWithDisposable:self.disposable];
return self;
}
這個RACPassthroughSubscriber包裹了RACSubject這個subscriber。
上述代碼<b>部分意思是如果RACMulticastConnection的冷信號sourceSignal的didSubscribe存在埋涧,就去執(zhí)行didSubscribe(subscriber)板辽。在示例代碼一中,冷信號的didSubscribe存在棘催,在didSubscribe執(zhí)行過程中劲弦,比如說“[subscriber sendNext:@1];”這一步,這里的sendNext調(diào)用的是RACPassthroughSubscriber的sendNext:
RACPassthroughSubscriber.m
- (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];
}
聯(lián)系<a>部分的分析醇坝,"[self.innerSubscriber sendNext:value]"實際上就是對RACMulticastConnection的RACSubject類型的熱信號signal發(fā)送sendNext消息邑跪,
RACSubject.m
- (void)sendNext:(id)value {
[self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:value];
}];
}
在示例代碼一的流程<2>中,RACMulticastConnection的熱信號signal并沒有訂閱者,所以上述sendNext方法沒有任何效果画畅。
步驟<3> 對RACMulticastConnection的熱信號signal進行訂閱
在步驟<2>中砸琅,RACMulticastConnection的熱信號signal就開始對冷信號sourceSignal進行訂閱了,冷信號sourceSignal的didSubscribe就開始執(zhí)行了夜赵,但此時熱信號signal沒有任何訂閱者明棍,所以冷信號的sendNext沒有接受者。
步驟<3>對熱信號signal進行了訂閱寇僧,于是此時熱信號signal有了訂閱者摊腋,當didSubscribe中再次sendNext時,上述RACSubject的sendNext方法就會向訂閱者發(fā)送值了嘁傀。
總結(jié)
根據(jù)上述分析兴蒸,重新對冷熱信號進行總結(jié):
1. Hot Observable是主動的,在不斷Push信息细办,不管有沒有人訂閱橙凳;而Cold Observable是被動的,需要去訂閱來Pull信息笑撞。
2. Hot Observable由于RACSubject的sendNext可以對多個訂閱者發(fā)內(nèi)容岛啸,所以是一對多,訂閱者共享信息茴肥;而Cold Observable只能一對一坚踩,當有不同的訂閱者,消息是重新完整發(fā)送瓤狐。
上述分析涉及到的源碼瞬铸,這里也進行總結(jié):
1. RACMulticastConnection的sourceSignal屬性是冷信號,signal屬性是熱信號础锐,connect方法就是使用熱信號對冷信號進行訂閱嗓节。
2. RACPassthroughSubscriber是對訂閱者進行了一層包裹封裝,將訂閱者保存在了自己的innerSubscriber屬性中皆警,而RACPassthroughSubscriber的sendNext又會調(diào)用innerSubscriber的sendNext拦宣。
所以RACPassthroughSubscriber是一個適配器,適配實現(xiàn)<RACSubscriber>接口的不同類型訂閱者信姓。
3. RACSubject實現(xiàn)的sendNext是對其所有訂閱者發(fā)送sendNext消息恢着;RACSubscriber的sendNext實現(xiàn)的sendNext是執(zhí)行nextBlock(value)。所以需要RACPassthroughSubscriber這樣一個適配器類封裝各自實現(xiàn)<RACSubscriber>的類财破。C