细说 ReactiveCocoa 的冷信号与热信号(三):怎么处理冷信号与
原文地址:http://tech.meituan.com/talk-about-reactivecocoas-cold-signal-and-hot-signal-part-3.html 第一篇文章中我们介绍了冷信号与热信号的概念,前一篇文章我们也讨论了为什么要区分冷信号与热信号,下面我会先为大家揭晓热信号的本质,再给出冷信号转换成热信号的方法。 揭示热信号的本质 在ReactiveCocoa中,究竟什么才是热信号呢?冷信号是比较常见的, 在RAC2.5文档的框架概述中,有着这样一段描述:
从这段描述中,我们可以发现Subject具备如下三个特点:
从第三个特点来看,Subject具备为未来订阅者缓冲事件的能力,那也就说明它是自身是有状态的。根据上文的介绍,Subject是符合热信号的特点的。为了验证它,我们再来做个简单实验: RACSubject *subject = [RACSubject subject]; RACSubject *replaySubject = [RACReplaySubject subject]; [[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{ // Subscriber 1 [subject subscribeNext:^(id x) { NSLog(@"Subscriber 1 get a next value: %@ from subject",x); }]; [replaySubject subscribeNext:^(id x) { NSLog(@"Subscriber 1 get a next value: %@ from replay subject",x); }]; // Subscriber 2 [subject subscribeNext:^(id x) { NSLog(@"Subscriber 2 get a next value: %@ from subject",x); }]; [replaySubject subscribeNext:^(id x) { NSLog(@"Subscriber 2 get a next value: %@ from replay subject",x); }]; }]; [[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{ [subject sendNext:@"send package 1"]; [replaySubject sendNext:@"send package 1"]; }]; [[RACScheduler mainThreadScheduler] afterDelay:1.1 schedule:^{ // Subscriber 3 [subject subscribeNext:^(id x) { NSLog(@"Subscriber 3 get a next value: %@ from subject",x); }]; [replaySubject subscribeNext:^(id x) { NSLog(@"Subscriber 3 get a next value: %@ from replay subject",x); }]; // Subscriber 4 [subject subscribeNext:^(id x) { NSLog(@"Subscriber 4 get a next value: %@ from subject",x); }]; [replaySubject subscribeNext:^(id x) { NSLog(@"Subscriber 4 get a next value: %@ from replay subject",x); }]; }]; [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{ [subject sendNext:@"send package 2"]; [replaySubject sendNext:@"send package 2"]; }]; 按照时间线来解读一下上述代码:
subject 与replaySubject 这两个subject。
接下来看一下输出的结果: 2015-09-28 13:35:22.855 RACDemos[13646:1269269] Start 2015-09-28 13:35:23.856 RACDemos[13646:1269269] Subscriber 1 get a next value: send package 1 from subject 2015-09-28 13:35:23.856 RACDemos[13646:1269269] Subscriber 2 get a next value: send package 1 from subject 2015-09-28 13:35:23.857 RACDemos[13646:1269269] Subscriber 1 get a next value: send package 1 from replay subject 2015-09-28 13:35:23.857 RACDemos[13646:1269269] Subscriber 2 get a next value: send package 1 from replay subject 2015-09-28 13:35:24.059 RACDemos[13646:1269269] Subscriber 3 get a next value: send package 1 from replay subject 2015-09-28 13:35:24.059 RACDemos[13646:1269269] Subscriber 4 get a next value: send package 1 from replay subject 2015-09-28 13:35:25.039 RACDemos[13646:1269269] Subscriber 1 get a next value: send package 2 from subject 2015-09-28 13:35:25.039 RACDemos[13646:1269269] Subscriber 2 get a next value: send package 2 from subject 2015-09-28 13:35:25.039 RACDemos[13646:1269269] Subscriber 3 get a next value: send package 2 from subject 2015-09-28 13:35:25.040 RACDemos[13646:1269269] Subscriber 4 get a next value: send package 2 from subject 2015-09-28 13:35:25.040 RACDemos[13646:1269269] Subscriber 1 get a next value: send package 2 from replay subject 2015-09-28 13:35:25.040 RACDemos[13646:1269269] Subscriber 2 get a next value: send package 2 from replay subject 2015-09-28 13:35:25.040 RACDemos[13646:1269269] Subscriber 3 get a next value: send package 2 from replay subject 2015-09-28 13:35:25.040 RACDemos[13646:1269269] Subscriber 4 get a next value: send package 2 from replay subject 结合结果可以分析出如下内容:
replaySubject 创建完毕。
只关注
经过观察不难发现,4个订阅者实际上是共享 对比上面两张图,是不是可以发现, 下面再来看看 将图3与图1对比会发现, 看到这里,我们终于揭开了热信号的面纱,结论就是:
RACSubject 及其子类是热信号。
如何将一个冷信号转化成热信号——广播 冷信号与热信号的本质区别在于是否保持状态,冷信号的多次订阅是不保持状态的,而热信号的多次订阅可以保持状态。所以一种将冷信号转换为热信号的方法就是,将冷信号订阅,订阅到的每一个时间通过 观察下面的代码: RACSignal *coldSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@"Cold signal be subscribed."); [[RACScheduler mainThreadScheduler] afterDelay:1.5 schedule:^{ [subscriber sendNext:@"A"]; }]; [[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{ [subscriber sendNext:@"B"]; }]; [[RACScheduler mainThreadScheduler] afterDelay:5 schedule:^{ [subscriber sendCompleted]; }]; return nil; }]; RACSubject *subject = [RACSubject subject]; NSLog(@"Subject created."); [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{ [coldSignal subscribe:subject]; }]; [subject subscribeNext:^(id x) { NSLog(@"Subscriber 1 recieve value:%@.",x); }]; [[RACScheduler mainThreadScheduler] afterDelay:4 schedule:^{ [subject subscribeNext:^(id x) { NSLog(@"Subscriber 2 recieve value:%@.",x); }]; 执行顺序是这样的:
coldSignal 。该信号声明了“订阅后1.5秒发送‘A’,3秒发送'B',5秒发送完成事件”。
如果所料不错的话,通过订阅这个 2015-09-28 19:36:45.703 RACDemos[14110:1556061] Subject created. 2015-09-28 19:36:47.705 RACDemos[14110:1556061] Cold signal be subscribed. 2015-09-28 19:36:49.331 RACDemos[14110:1556061] Subscriber 1 recieve value:A. 2015-09-28 19:36:50.999 RACDemos[14110:1556061] Subscriber 1 recieve value:B. 2015-09-28 19:36:50.999 RACDemos[14110:1556061] Subscriber 2 recieve value:B. 参考时间线,会得到下图: 不难发现其中的几个重点:
Subscriber 1 是subject 创建后就开始订阅的,但是第一个接收时间与coldSignal 第一个值的时间是一样的。
通过观察可以确定, 当然,使用这种 - (RACMulticastConnection *)publish; - (RACMulticastConnection *)multicast:(RACSubject *)subject; - (RACSignal *)replay; - (RACSignal *)replayLast; - (RACSignal *)replayLazily; 这5个方法中,最为重要的就是 /// implementation RACSignal (Operations) - (RACMulticastConnection *)multicast:(RACSubject *)subject { [subject setNameWithFormat:@"[%@] -multicast: %@",self.name,subject.name]; RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject]; return connection; } /// implementation 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; } #pragma mark Connecting - (RACDisposable *)connect { BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0,1,&_hasConnected); if (shouldConnect) { self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal]; } return self.serialDisposable; } - (RACSignal *)autoconnect { __block volatile int32_t subscriberCount = 0; return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) { OSAtomicIncrement32Barrier(&subscriberCount); RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber]; RACDisposable *connectionDisposable = [self connect]; return [RACDisposable disposableWithBlock:^{ [subscriptionDisposable dispose]; if (OSAtomicDecrement32Barrier(&subscriberCount) == 0) { [connectionDisposable dispose]; } }]; }] setNameWithFormat:@"[%@] -autoconnect",self.signal.name]; } 虽然代码比较短但不是很好懂,大概来说明一下:
RACSignal 类的实例调用- (RACMulticastConnection *)multicast:(RACSubject *)subject 时,以self 和subject 作为构造参数创建一个RACMulticastConnection 实例。
由于RAC是一个线程安全的框架,所以好奇的同学可以了解下“OSAtomic*”这一系列的原子操作。抛开这些应该不难理解上述代码。 了解源码之后,这个方法的正确使用就清楚了,应该像这样: RACSignal *coldSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@"Cold signal be subscribed."); [[RACScheduler mainThreadScheduler] afterDelay:1.5 schedule:^{ [subscriber sendNext:@"A"]; }]; [[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{ [subscriber sendNext:@"B"]; }]; [[RACScheduler mainThreadScheduler] afterDelay:5 schedule:^{ [subscriber sendCompleted]; }]; return nil; }]; RACSubject *subject = [RACSubject subject]; NSLog(@"Subject created."); RACMulticastConnection *multicastConnection = [coldSignal multicast:subject]; RACSignal *hotSignal = multicastConnection.signal; [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{ [multicastConnection connect]; }]; [hotSignal subscribeNext:^(id x) { NSLog(@"Subscribe 1 recieve value:%@.",x); }]; [[RACScheduler mainThreadScheduler] afterDelay:4 schedule:^{ [hotSignal subscribeNext:^(id x) { NSLog(@"Subscribe 2 recieve value:%@.",x); }]; }]; 或者这样: RACSignal *coldSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@"Cold signal be subscribed."); [[RACScheduler mainThreadScheduler] afterDelay:1.5 schedule:^{ [subscriber sendNext:@"A"]; }]; [[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{ [subscriber sendNext:@"B"]; }]; [[RACScheduler mainThreadScheduler] afterDelay:5 schedule:^{ [subscriber sendCompleted]; }]; return nil; }]; RACSubject *subject = [RACSubject subject]; NSLog(@"Subject created."); RACMulticastConnection *multicastConnection = [coldSignal multicast:subject]; RACSignal *hotSignal = multicastConnection.autoconnect; [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{ [hotSignal subscribeNext:^(id x) { NSLog(@"Subscribe 1 recieve value:%@.",x); }]; }]; [[RACScheduler mainThreadScheduler] afterDelay:4 schedule:^{ [hotSignal subscribeNext:^(id x) { NSLog(@"Subscribe 2 recieve value:%@.",sans-serif;"> 以上的两种写法和之前用Subject来传递的例子都可以得到相同的结果。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |