美文网首页
ReactiveCocoa技术讲解-第三讲信号的高阶操作

ReactiveCocoa技术讲解-第三讲信号的高阶操作

作者: 好雨知时节浩宇 | 来源:发表于2017-12-10 12:28 被阅读31次
    SwitchToLatest:

    1、当新的值信号产生的时候,立即订阅最新的值信号。同时把前一个值信号关闭掉,取消订阅。
    2、降维后新信号的结束时间:取决于信号最后一个值信号的结束时间。
    3、降维后新信号的异常处理:只要有一个值信号产生异常,就会传递下来。
    4、图示:

    switchToLatest.png
    5、应用示例:后续添加,暂时在项目中未摘出 。
    //敬请期待
    
    if/then/else

    1、作用描述:每次为Yes是则从信号A的第一个值开始读取信号,知道下一个值为NO时,则从信号B的第一个值开始读取信号。依次反复。每次读取从是从相应信号的第一个值开始。
    2、图示:

    if:then:else.png
    3、if/then/else的本质:
    实质就是先把boolSignal做一个map操作,当为yes时,返回trueSignal,当为No时,返回falseSignal,所以最终我们得到一个高阶信号。详情见下图:
    if:then:else实质.png
    4、使用示例:
    Flatten

    1、扁平,通常用于高阶信号的降阶操作
    2、图示:


    flatten.png
    Flatten:(NSUInteger)

    1、作用:同Flatten作用一致,只不过这里设定了对应参数值。
    2、参数值:作用是设定当前栈中的信号数量。flatten对信号做降阶操作时,内部会维护两个栈:当前栈等待栈
    (2.1)当前栈中用来存放正在订阅的信号,当前栈中的信号只能等信号完成后信号才会从栈中退出。
    (2.2)等待栈中用来存放 已经产生,但是还没有进行订阅处理的信号。
    3、图示:

    Flatten变形.png
    4、Flatten相关的信号处理函数需要结合实际例子仔细体会。
    Concat:

    1、上一篇已经讲过concat,这次要从concat的实现上来说一下。
    2、concat函数的实现:是通过flatten来封装的。即:concat <=> flatten:1
    3、concat两个函数:

    • 3.1)concat 高阶多维信号通常使用这个函数
    • 3.2) concat:这个函数的实现看源码会发现略有不同。并不是通过flatten来实现。

    4、使用示例:实现一个延迟一秒的信号

    RACSignal *signal = @[@1, @3, @7,@9, @8].rac_sequence.signal;
    RACSignal *timerSignal = [[signal map:^id(id value) {
                     return [[RACSignal return:value] delay:1];
    }] concat];
    
    FlattenMap

    1、FlattenMap的实现是基于bind操作,它的实现是:传入一个block,然后将这个block作用于原始信号传出的值上,并生成一个新的信号。(新生成的信号是bind函数根据原始信号的值创建的)
    2、FlattenMap的实现:

    - (instancetype)flattenMap:(RACStream * (^)(id value))block {
        Class class = self.class;
    
        return [[self bind:^{
            return ^(id value, BOOL *stop) {
                id stream = block(value) ?: [class empty];
                NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);
    
                return stream;
            };
        }] setNameWithFormat:@"[%@] -flattenMap:", self.name];
    }
    

    3、FlattenMap的重要性:

     (1)很多信号的实现是基于flattenMap,例如:flatten、map、filter
    
    //flatten
        RACSignal *flatten = [signalA flattenMap:^RACStream *(RACSignal *value) {
            return value;
        }];
    //map
        RACSignal *map = [signalA flattenMap:^RACStream *(id value) {
            id anthorValue = value;
            return [RACSignal return:anthorValue];
        }];
    //filter
        RACSignal *filter = [signalA flattenMap:^RACStream *(id value) {
            BOOL filter = (value == nil);
            return filter ? [RACSignal empty] :[RACSignal return:value];
        }]
    
     (2) 支持串行异步操作。
    
      RACSignal *signal = [RACSignal return:@"http://xx.com/a"];
    RACSignal *getSignal = [signal flattenMap: ^RACStream *(NSString *url) {
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
        return [NSURLConnection rac_sendAsynchronousRequest:request];
    }];
    RACSignal *jsonSignal = [getSignal flattenMap: ^RACStream *(NSData *data) {
        NSError *error = nil;
        id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
        return error == nil ? [RACSignal return: result] : [RACSignal error: error];
    }];
    RACSignal *getItemSignal = [jsonSignal flattenMap: ^RACStream *(NSDictionary *value) {
        if (![value isKindOfClass:[NSDictionary class]] || value[@"data.url"] == nil) {
            return [RACSignal error:someError];
        }
        NSURLRequest *anotherRequest = [NSURLRequest requestWithURL:
                                        [NSURL URLWithString:value[@"data.url"]]];
        return [NSURLConnection rac_sendAsynchronousRequest:anotherRequest];
    }];
    
     (3)bind相关
    

    4、RAC很多函数基层都是通过bind来实现的(bind是一个十分重要的函数),FlattenMap实现也是通过bind实现,相当于是中间层,上层会基于FlattenMap封装提供map等函数,供使用者调用,有时也直接调用FlattenMap。
    5、bind函数的简单实现:
    原理讲解:
    (1)在最外层创建一个信号,并返回值外界
    (2)订阅原始信号
    (3)在内部根据传入的block和原始信号的值x,创建一个信号,并由最外层的订阅者来订阅。
    (4) 将原始信号产生值、error、complet全部传给最外层订阅者。

    6、bind函数代码示例:

    //bind 简单实现
    
    - (RACSignal *)bindSample:(RACStreamBindBlock (^)(void))block {
        //1、在最外层创建信号,并返回。
        RACSignal *newSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            RACStreamBindBlock bindBlock = block();
            RACSignal *selfSignal;
            //2、订阅原始信号
            [self subscribeNext:^(id x) {
                BOOL stop = NO;
                //3、根据原始信号的信号值,生成一个新生成一个信号。
                RACSignal *signal = (RACSignal *)bindBlock(x,&stop);
                //3.1、判断信号是否存在和是否停止,如果不存在,直接将消息交给最外层订阅者
                if(signal == nil || stop == NO) {
                    [subscriber sendCompleted];
                }
                else { //3.2、对新创建的信号进行订阅,并将值传递给最外层的订阅者
                    [signal subscribeNext:^(id x) {
                        [subscriber sendNext:x];
                    } error:^(NSError *error) {
                        [subscriber sendError:nil];
                    } completed:^{
                        //do nothing
                    }];
                }
            } error:^(NSError *error) {
                [subscriber sendError:nil];
            } completed:^{
                [subscriber sendCompleted];
            }];
            return nil; //return nil
        }];
        return newSignal;
    }
    
    FlattenMap 与 Map 对比(简单做下对比,详细内容,可在查阅相关资料)

    1、block参数返回值:

    • Map:block参数返回的是id类型的值
    • FlattenMap:返回的是signal。

    2、内部实现:

    • Map的内部实现:实际上是通过FlattenMap实现的。
    • FlattenMap内部实现:是基于bind函数来完成的,上面有bind的简单实现。

    3、应用:

    • FlattenMap通常用于高阶信号
    • Map通常用于一维信号

    相关文章

      网友评论

          本文标题:ReactiveCocoa技术讲解-第三讲信号的高阶操作

          本文链接:https://www.haomeiwen.com/subject/infsixtx.html