菜鸟运用RAC对replay,replayLast,replayLazily记录(三)

前言

在上一篇文章中
菜鸟开始学习ReactiveObjc运用的记录(二)
自己已经对RAC的一些基本概念有了一点了解,当然自己了解的远不止那一篇中写的东西。RAC的学习对于我来说还是很有难度的,每当遇到不懂的问题点,就是自己百度学习。继上篇后,心里就想着用RAC怎样重写网络封装。
写网络请求的过程中,发现无非是把网络请求封装成Command,也就是如下格式:

- (RACSignal *)requestNetworkData:(HttpBaseRequest *)req{
     /// request 必须的有值  容错
    if (!req) return [RACSignal error:[NSError errorWithDomain:HTTPServiceErrorDomain code:-1 userInfo:nil]];
     
    @weakify(self);
    /// 创建信号
    RACSignal *signal = [RACSignal createSignal:^(id subscriber) {
        @strongify(self);
        /// 获取request
        NSError *serializationError = nil;
        NSMutableURLRequest *request = [self.manager.requestSerializer requestWithMethod:req.method URLString:[[NSURL URLWithString:req.path relativeToURL:[NSURL URLWithString:BaseUrl] ] absoluteString] parameters:req.parameters error:&serializationError];
         
        if (serializationError) {
            dispatch_async(self.manager.completionQueue ?: dispatch_get_main_queue(), ^{
                [subscriber sendError:serializationError];
            });
            return [RACDisposable disposableWithBlock:^{
            }];
        }
        /// 获取请求任务
        __block NSURLSessionDataTask *task = nil;
        task = [self.manager dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
             @strongify(self);
            if (error) {
                NSError *parseError = [self errorFromRequestWithTask:task httpResponse:(NSHTTPURLResponse *)response responseObject:responseObject error:error];
     
                NSInteger code = [parseError.userInfo[HTTPServiceErrorHTTPStatusCodeKey] integerValue];
                NSString *msgStr = parseError.userInfo[HTTPServiceErrorDescriptionKey];
                //初始化、返回数据模型
                HttpBaseResonse *response = [[HttpBaseResonse alloc] initWithResponseError:parseError code:code msg:msgStr];
              
                [subscriber sendNext:response];
                [subscriber sendCompleted];
                //错误可以在此处处理
                 
            } else {
               
                /// 判断
                NSInteger statusCode = [responseObject[HTTPServiceResponseCodeKey] integerValue];
                if (statusCode == HTTPResponseCodeSuccess) {
                    FMHttpResonse *response = [[FMHttpResonse alloc] initWithResponseSuccess:responseObject[HTTPServiceResponseDataKey] code:statusCode];
                    
                    [subscriber sendNext:response];
                    [subscriber sendCompleted];
                     
                }else{
                    if (statusCode == HTTPResponseCodeNotLogin) {
                   
         
                    }else{
                        NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                        userInfo[HTTPServiceErrorResponseCodeKey] = @(statusCode);
                        //取出服务给的提示
                        NSString *msgTips = responseObject[HTTPServiceResponseMsgKey];
                        //服务器没有返回,错误信息
                        if ((msgTips.length == 0 || msgTips == nil || [msgTips isKindOfClass:[NSNull class]])) {
                            msgTips = @"服务器出错了,请稍后重试~";
                        }
                         
                        userInfo[HTTPServiceErrorMessagesKey] = msgTips;
                        if (task.currentRequest.URL != nil) userInfo[HTTPServiceErrorRequestURLKey] = task.currentRequest.URL.absoluteString;
                        if (task.error != nil) userInfo[NSUnderlyingErrorKey] = task.error;
                        NSError *requestError = [NSError errorWithDomain:HTTPServiceErrorDomain code:statusCode userInfo:userInfo];
                        //错误信息反馈回去了、可以在此做响应的弹窗处理,展示出服务器给我们的信息
                        FMHttpResonse *response = [[FMHttpResonse alloc] initWithResponseError:requestError code:statusCode msg:msgTips];
                 
                        [subscriber sendNext:response];
                        [subscriber sendCompleted];
                        //错误处理
                    }
                }
            }
        }];
         
        /// 开启请求任务
        [task resume];
        return [RACDisposable disposableWithBlock:^{
            [task cancel];
        }];
    }];
    return [signal replayLazily]; //多次订阅同样的信号,执行一次
}

在看这种写法的时候对我还是很熟悉的,以前没用RAC的时候也是这种模式很像,但让我感兴趣的是return [signal replayLazily]; //多次订阅同样的信号,执行一次这一句。因为RAC中有 replay,replayLast,ReplayLazily这三个用法。所以疑惑之。

思考

当网上看到大神写的一篇Comparing replay, replayLast, and replayLazily心里如获至宝。自己也只是照着大神写的栗子细细体会。才发现之前自己都没有懂得这么细节。
为什么会有replay, replayLast, and replayLazily的出现
看下面的栗子1

-(void)exampleOne
{
    __block int num = 0;
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id  subscriber) {
        num++;
        NSLog(@"Increment num to: %i", num);
        [subscriber sendNext:@(num)];
        return nil;
    }];
    
    NSLog(@"Start subscriptions");
    
    // Subscriber 1 (S1)
    [signal subscribeNext:^(id x) {
        NSLog(@"S1: %@", x);
    }];
    
    // Subscriber 2 (S2)
    [signal subscribeNext:^(id x) {
        NSLog(@"S2: %@", x);
    }];
    
    // Subscriber 3 (S3)
    [signal subscribeNext:^(id x) {
        NSLog(@"S3: %@", x);
    }];
    
}
输出结果1.png

结论:每次订阅都会重新执行订阅代码 之前我了解RAC的时候都没注意到这一点。
粟子2演示信号被添加订阅的时候,订阅者是怎么接收发送的值的

-(void)exampleTwo
{
    RACSubject *letters = [RACSubject subject];
    RACSignal *signal = letters;
    
    NSLog(@"Subscribe S1");
    [signal subscribeNext:^(id x) {
        NSLog(@"S1: %@", x);
    }];
    
    NSLog(@"Send A");
    [letters sendNext:@"A"];
    NSLog(@"Send B");
    [letters sendNext:@"B"];
    
    NSLog(@"Subscribe S2");
    [signal subscribeNext:^(id x) {
        NSLog(@"S2: %@", x);
    }];
    
    NSLog(@"Send C");
    [letters sendNext:@"C"];
    NSLog(@"Send D");
    [letters sendNext:@"D"];
    
    NSLog(@"Subscribe S3");
    [signal subscribeNext:^(id x) {
        NSLog(@"S3: %@", x);
    }];
    [letters sendNext:@"E"];
}
输出结果:
Subscribe S1
Send A
S1: A
Send B
S1: B
Subscribe S2
Send C
S1: C
S2: C
Send D
S1: D
S2: D
Subscribe S3
S1: E
S2: E
S3: E

总结:当信号被多次订阅时,就会有多次接收

栗子1:不满足我希望在网络中应用的场景- 无论有多少个监听者,请求只发送一下
栗子2:不满足场景希望拿到订阅前信号发送过的值

replay

Signal这个replay方法将返回一个新的信号,当源信号被订阅时,会立即发送给订阅者全部历史的值,不会重复执行源信号中的订阅代码,不仅如此,订阅者还将收到所有未来发送过去的值。
栗子3


-(void)exampleThree
{
    __block id sub = nil;
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id  subscriber) {
        
        NSLog(@"我被订阅了,%@", subscriber); ////用到replay 此Block立即会被调用一次,此后不会调用
        sub = subscriber; //将发送者保存到外部变量,因为block外部需要发送信号
        return nil;
    }] replay];
    
    NSLog(@"----发送消息A------");
    [sub sendNext:@"消息A"]; ////此时此刻发送成功,因为上面Block已经执行了,sub有值
    NSLog(@"Start 1 subscriptions");
    // Subscriber 1 (S1)
    [signal subscribeNext:^(id x) {  //收到上面的历史消息A
        NSLog(@"S1: %@", x);
    }];
    NSLog(@"--------发送消息B------------");
    [sub sendNext:@"消息B"];
    NSLog(@"--------发送消息C------------");
    [sub sendNext:@"消息C"];
    // Subscriber 3 (S3)
    NSLog(@"Start 2 subscriptions");
    [signal subscribeNext:^(id x) { //订阅就会收到历史消息A,B,C
        NSLog(@"S2: %@", x);
    }];
    NSLog(@"--------发送消息D------------");
    [sub sendNext:@"消息D"]; //前面的订阅都能收到消息D
    
}

//输出结果:
我被订阅了,
----发送消息A------
Start 1 subscriptions
S1: 消息A
--------发送消息B------------
S1: 消息B
--------发送消息C------------
S1: 消息C
Start 2 subscriptions
S2: 消息A
S2: 消息B
S2: 消息C
--------发送消息D------------
S1: 消息D
S2: 消息D

replay总结:
1.调用replay,会导致源信号的订阅代码先被调用。
2.后续信号多次被订阅再也不会重复执行源信号的订阅代码了。
3.后面的订阅者能收到之前所有历史已经发送的值。和后续将来发送的所有消息。

replayLast

栗子4

-(void)exampleFour
{
    __block id sub = nil;
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id  subscriber) {
        
        NSLog(@"我被订阅了,%@", subscriber); ////用到replayLast 此Block立即会被调用一次,此后不会调用
        sub = subscriber; //将发送者保存到外部变量,因为block外部需要发送信号
        return nil;
    }] replayLast];
    
    NSLog(@"----发送消息A------");
    [sub sendNext:@"消息A"]; ////此时此刻发送成功,因为上面Block已经执行了,sub有值
    NSLog(@"Start 1 subscriptions");
    // Subscriber 1 (S1)
    [signal subscribeNext:^(id x) {  //收到上面的历史消息A
        NSLog(@"S1: %@", x);
    }];
    NSLog(@"--------发送消息B------------");
    [sub sendNext:@"消息B"];
    NSLog(@"--------发送消息C------------");
    [sub sendNext:@"消息C"];
    // Subscriber 3 (S3)
    NSLog(@"Start 2 subscriptions");
    [signal subscribeNext:^(id x) { //订阅就只会收到最近的历史消息C
        NSLog(@"S2: %@", x);
    }];
    NSLog(@"--------发送消息D------------");
    [sub sendNext:@"消息D"]; //前面的订阅都能收到消息D
    
}
     输出:
    我被订阅了,
----发送消息A------
Start 1 subscriptions
S1: 消息A
--------发送消息B------------
S1: 消息B
--------发送消息C------------
S1: 消息C
Start 2 subscriptions
S2: 消息C
--------发送消息D------------
S1: 消息D
S2: 消息D
     

这里replayLast与replay相比,重点是那个LastLast也就是最后的意思上面的栗子可以看出来,第二次订阅只收到了最后的那条历史消息C,而没有收到消息A,B,这里是和replay不同的地方

总结
1.调用replayLast,会导致源信号的订阅代码先被调用(与replay相同)
2.信号多次被订阅不会重复执行源信号的订阅代码(与replay相同)
3.订阅者只能收到历史已经发送的最后的那条信息(这与replay唯一不同)、以及未来发送的所有消息(与replay相同)。

replayLazily

栗子五:

-(void)exampleFive
{
    __block id sub = nil;
    RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id  subscriber) {
        
        NSLog(@"我被订阅了,%@", subscriber); ////首次订阅此信号导致此Block会被调用一次,此后不会调用
        sub = subscriber; //将发送者保存到外部变量,因为block外部需要发送信号
        return nil;
    }] replayLazily];
    
    NSLog(@"----发送消息A------");
    [sub sendNext:@"消息A"]; ////此时此刻发送失败,因为上面Block未执行,sub值是nil
     NSLog(@"Start 1 subscriptions");
    // Subscriber 1 (S1)
    [signal subscribeNext:^(id x) {  //第一次订阅就会触发源信号的Block,后面订阅不会触发
        NSLog(@"S1: %@", x);
    }];
    NSLog(@"--------发送消息B------------");
    [sub sendNext:@"消息B"];
    NSLog(@"--------发送消息C------------");
    [sub sendNext:@"消息C"];
    // Subscriber 3 (S3)
    NSLog(@"Start 2 subscriptions");
    [signal subscribeNext:^(id x) { //一订阅就会收到最近的历史消息C
        NSLog(@"S2: %@", x);
    }];
     NSLog(@"--------发送消息D------------");
    [sub sendNext:@"消息D"]; //前面的订阅都能收到消息D
}
    
//输出
----发送消息A------
Start 1 subscriptions
我被订阅了,
--------发送消息B------------
S1: 消息B
--------发送消息C------------
S1: 消息C
Start 2 subscriptions
S2: 消息B
S2: 消息C
--------发送消息D------------
S1: 消息D
S2: 消息D

总结
1.调用replayLazily,会导致源信号的订阅代码只在信号首次被订阅时调用(与replay唯一不同
2.信号多次被订阅不会重复执行源信号的订阅代码(与replay相同)
3.订阅者能收到所有历史已经发送的、未来发送的所有消息。(与replay相同)

replay,replayLast,replayLazily总结

relay与replayLast的区别是:后面的多次订阅,接收到的历史消息的不同,replayLast只接收最近的那一条历史消息

replay与replayLazily的区别是:源信号block的执行顺序不同,replay调用就直接执行了源信号block代码,而replayLazily在它第一次订阅后才执行源信号block代码

replayLast与replayLazily的区别是:1.后面的多次订阅,接收到的历史消息的不同,replayLast只接收最近的那一条历史消息,replayLazily却是全部接收,2.源信号block的执行顺序不同,replayLast调用就直接执行了源信号block代码,而replayLazily在它第一次订阅后才执行源信号block代码

你可能感兴趣的:(菜鸟运用RAC对replay,replayLast,replayLazily记录(三))