信号源
在RAC中,信号源代表等是随着时间而改变的值流,这是对RAC最精准的概括。订阅者可以通过订阅信号源来获取这些值:
Stream of values over time.
你可以把它想象成运送玻璃珠等管道,你打开阀门时,玻璃珠就一个一个的到达。这里玻璃珠就是所需要的值,打开阀门就是订阅它们的过程。
RACSignal
RACSignal 代表的是未来将会被传送的值,RACSignal可以向发送者发送三种不同类型的事件:
- next:通过next事件向订阅者传送新的值。并且这个值可以为nil;
- error: 通过error 事件向订阅者表明信号在正常结束前发送错误。
- completed:通过completed 事件向订阅者表明信号已经正常结束,不会再有后续的值传送给订阅者。
注意:一个信号的生命周期是任意多个next事件和一次error或completed事件组成。
冷信号
冷信号是被动的,只有当你订阅的时候,它才会发送消息。
冷信号是一对一的,当有不同的订阅者,消息是重新完整发送。
//创建一个signal
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
NSLog(@"signal 正在被创建");
[subscriber sendNext:@"1"];
return nil;
}];
// 以上我们创建了一个signal, 但它并没有被订阅。打印信息如下
//2016-05-23 18:08:05.730 HotColdSignal[2141:456678] signal 创建完成了
[signal subscribeNext:^(id x) {
NSLog(@"我来第一次订阅signal了--%@",x);
}];
//以上,我们订阅了signal,发现signal 被创建了
//2016-05-23 18:09:51.762 HotColdSignal[2145:457126] signal 正在被创建
//2016-05-23 18:09:51.762 HotColdSignal[2145:457126] 我来第一次订阅signal了--1
[signal subscribeNext:^(id x) {
NSLog(@"我来第二次订阅signal了--%@",x);
}];
//以上,我们又一次的订阅了signal,发现,signal 被创建了两次,以此类推,我们订阅几次signal, signal就会被创建几次
//2016-05-23 18:17:54.320 HotColdSignal[2155:459009] signal 正在被创建
//2016-05-23 18:17:54.321 HotColdSignal[2155:459009] 我来第一次订阅signal了--1
//2016-05-23 18:17:54.321 HotColdSignal[2155:459009] signal 正在被创建
//2016-05-23 18:17:54.321 HotColdSignal[2155:459009] 我来第二次订阅signal了--1
热信号
热信号是主动的,尽管没有被订阅,但是它会时时推送。
热信号是一对多的,一个信号可以有多个订阅者,共享订阅事件。
RACMulticastConnection *connection = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
NSLog(@"signal 正在被创建");
[subscriber sendNext:@"1"];
return nil;
}] publish];
[connection connect];
RACSignal *signal = connection.signal;
// 以上,我们创建了一个signal, 通过publish 方法将它转换为RACMulticastConnection(暂时不需要理解RACMulticastConnection是什么意思),然后通过RACMulticastConnection获取signal,运行代码我们发现,signal被创建了,但是我们并没有订阅signal
//2016-05-23 18:28:35.084 HotColdSignal[2167:461305] signal 正在被创建
修改一下代码
RACMulticastConnection *connection = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
NSLog(@"signal 正在被创建");
[[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
}];
return nil;
}] publish];
[connection connect];
RACSignal *signal = connection.signal;
[signal subscribeNext:^(id x) {
NSLog(@"我第一次来订阅signal了啊--%@",x);
}];
[signal subscribeNext:^(id x) {
NSLog(@"我第二次来订阅signal了啊--%@",x);
}];
// 运行代码我们发现与冷信号不一样的是,signal只被创建了一次
//2016-05-23 18:43:55.924 HotColdSignal[2218:466419] signal 正在被创建
//2016-05-23 18:43:56.985 HotColdSignal[2218:466419] 我第一次来订阅signal了啊--1
//2016-05-23 18:43:56.986 HotColdSignal[2218:466419] 我第二次来订阅signal了啊--1
为什么要区分热信号与冷信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
NSLog(@"signal 正在被创建");
//假如这里是一个网络请求呢?
NSDictionary *dic = @{
@"title1" : @"title1",
@"title2" : @"title2",
@"title3" : @"title3"
};
[subscriber sendNext:dic];
[subscriber sendCompleted];
return nil;
}];
RACSignal *title1 = [signal flattenMap:^RACStream *(NSDictionary *dic) {
if ([[dic valueForKey:@"title1"] isKindOfClass:[NSString class]]) {
return [RACSignal return:[dic valueForKey:@"title1"]];
}
else
{
return [RACSignal error:[NSError errorWithDomain:@"获取title1错误" code:404 userInfo:@{
@"value" :dic
}]];
}
}];
RACSignal *title2 = [signal flattenMap:^RACStream *(NSDictionary *dic) {
if ([[dic valueForKey:@"title2"] isKindOfClass:[NSString class]]) {
return [RACSignal return:[dic valueForKey:@"title2"]];
}
else
{
return [RACSignal error:[NSError errorWithDomain:@"获取title2错误" code:404 userInfo:@{
@"value" :dic
}]];
}
}];
RACSignal *title3 = [signal flattenMap:^RACStream *(NSDictionary *dic) {
if ([[dic valueForKey:@"title3"] isKindOfClass:[NSString class]]) {
return [RACSignal return:[dic valueForKey:@"title3"]];
}
else
{
return [RACSignal error:[NSError errorWithDomain:@"获取title3错误" code:404 userInfo:@{
@"value" :dic
}]];
}
}];
RAC(self.label1, text) = [[title1 catchTo:[RACSignal return:@"Error"]] startWith:@"Loading..."];
RAC(self.label2, text) = [[title2 catchTo:[RACSignal return:@"Error"]] startWith:@"Loading..."];
RAC(self.label3, text) = [[title3 catchTo:[RACSignal return:@"Error"]] startWith:@"Loading..."];
[[RACSignal merge:@[title1, title2, title3]] subscribeError:^(NSError *error) {
NSLog(@"错误信息要集中处理啊~~");
}];
//2016-05-24 11:13:57.980 HotColdSignal[537:127936] signal 正在被创建
//2016-05-24 11:13:57.982 HotColdSignal[537:127936] signal 正在被创建
//2016-05-24 11:13:57.983 HotColdSignal[537:127936] signal 正在被创建
//2016-05-24 11:13:57.983 HotColdSignal[537:127936] signal 正在被创建
//2016-05-24 11:13:57.983 HotColdSignal[537:127936] signal 正在被创建
//2016-05-24 11:13:57.983 HotColdSignal[537:127936] signal 正在被创建
查看以上代码:运行一下可以发现signal被创建了6次。
因为我们的数据是在sinal中处理的,你可以把它拿在signal外,避免这样的事发生,但你不能否认,业务中我们会将一个网络请求封装成一个singal, 然后去处理返回的结果。这就造成了一个问题,网络请求发送了六次,在更复杂的业务逻辑中可能会更多,当然对于用户还是服务器而言,我们都不希望频繁的发送网络请求。这就是冷信号的弊端,而这一切都可以将冷信号转换为热信号解决。
这里有一个很重要的结论:任何的信号转换(flapmap, merge 等)即是对原有的信号进行订阅从而产生新的信号。
如何处理冷热信号
结论:
- RACSubject及其子类是热信号。
- RACSignal排除RACSubject类以外的是冷信号。
通过以下方法,将冷信号转换为热信号
- (RACMulticastConnection *)publish;
- (RACMulticastConnection *)multicast:(RACSubject *)subject;
- (RACSignal *)replay;
- (RACSignal *)replayLast;
- (RACSignal *)replayLazily;
参考资料
http://blog.leichunfeng.com/blog/2015/12/25/reactivecocoa-v2-dot-5-yuan-ma-jie-xi-zhi-jia-gou-zong-lan/
http://tech.meituan.com/tag/ReactiveCocoa