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

SwitchToLatest:

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

ReactiveCocoa技术讲解-第三讲信号的高阶操作_第1张图片
switchToLatest.png

5、应用示例:后续添加,暂时在项目中未摘出 。

//敬请期待
if/then/else

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

ReactiveCocoa技术讲解-第三讲信号的高阶操作_第2张图片
if:then:else.png

3、if/then/else的本质:
实质就是先把boolSignal做一个map操作,当为yes时,返回trueSignal,当为No时,返回falseSignal,所以最终我们得到一个高阶信号。详情见下图:
ReactiveCocoa技术讲解-第三讲信号的高阶操作_第3张图片
if:then:else实质.png

4、使用示例:

Flatten

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


ReactiveCocoa技术讲解-第三讲信号的高阶操作_第4张图片
flatten.png
Flatten:(NSUInteger)

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

ReactiveCocoa技术讲解-第三讲信号的高阶操作_第5张图片
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 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技术讲解-第三讲信号的高阶操作)