ReactiveCocoa 信号

信号源

在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

你可能感兴趣的:(ReactiveCocoa 信号)