ReactiveCocoa信号组合操作merge和zip

1. merge

merge的源代码进行转换后如下所示:

+ (RACSignal *)merge:(id<NSFastEnumeration>)signals {
    NSMutableArray *copiedSignals = [[NSMutableArray alloc] init];
    for (RACSignal *signal in signals) {
        [copiedSignals addObject:signal];
    }
    
    RACSignal *tempSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
        for (RACSignal *signal in copiedSignals) {
            [subscriber sendNext:signal];
        }
        
        [subscriber sendCompleted];
        return nil;
    }];
    
    RACSignal *rtSignal = [tempSignal flattenMap:^(id value) {
        NSCAssert([value isKindOfClass:RACStream.class], @"Stream %@ being flattened contains an object that is not a stream: %@", stream, value);
        return value;
    }];
    
    return rtSignal;
}
  1. tempSignal这个信号的didSubscribe仅仅是将信号数组中的信号signali一个个传递出去;
  2. 对tempSignal进行flattenMap转换仅仅是将tempSignal传递的值signali直接传递到下一步,没有额外操作。
  3. 在订阅rtSignal时,rtSignal的didSubscribe会将flattenMap传递出来的每一个signali进行订阅,将结果传递出去。

其实merge的作用就如同它的名字一样,是将多个信号合并成一个信号;对合并后的信号进行订阅时,原来的信号都会被订阅,而这些信号在sendNext时也都会执行同一个next。

2. zip

/// Zips the values in the given streams to create RACTuples.
///
/// The first value of each stream will be combined, then the second value, and
/// so forth, until at least one of the streams is exhausted.
///
/// streams - The streams to combine. These must all be instances of the same
///           concrete class implementing the protocol. If this collection is
///           empty, the returned stream will be empty.
///
/// Returns a new stream containing RACTuples of the zipped values from the
/// streams.
+ (instancetype)zip:(id<NSFastEnumeration>)streams;

先翻译一下zip的作用,给定一个信号数组signal_array[N],创建一个信号zip_return,当订阅zip_return时,会等待signal_array中每一个信号都sendNext:valuei后,zip_return才会sendNext,zip_return传出的值是[value1,...,valueN]。

看一下zip的源码:

+ (instancetype)zip:(id<NSFastEnumeration>)streams {
    return [[self join:streams block:^(RACStream *left, RACStream *right) {
        return [left zipWith:right];
    }] setNameWithFormat:@"+zip: %@", streams];
}

上面代码涉及到join和zipWith两个方法,先看zipWith方法。

2.1 zipWith

RACSignal.m

- (instancetype)zipWith:(RACStream *)stream
  1. zipWith返回一个信号zipWith_return_signal;
  2. 在订阅zipWith_return_signal时,当且仅当当前信号self和传入的参数信号signal都sendNext值时,才会将获取到的值(以一个tuple形式,[value_self,value_signal],self信号传出的值在前,signal信号传出的值在后)吐出去;
  3. 当前信号self或入参信号signal发出complete时,zipWith_return_signal"都会"sendCompleted.

zipWith有个坑,就是zipWith的两个信号sendNext的数目以sendNext数目最少的信号为准,什么意思呢,看个具体例子:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    
    RACSignal *zippedSignal = [RACSignal zip:@[[self fetchData1],
                                               [self fetchData3]]];
    
     [zippedSignal subscribeNext:^(RACTuple *tuple) {
         NSLog(@"%@", tuple);
     }];
    
}

- (RACSignal *)fetchData1 {
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [self httpRequest:@"Post" param:@{@"commandKey":@"request1"} completion:^(id response) {
            [subscriber sendNext:@11];
            [subscriber sendNext:@22];
            [subscriber sendCompleted];
        }];
        return nil;
    }];
}

- (RACSignal *)fetchData3 {
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [self httpRequest:@"Post" param:@{@"commandKey":@"request3"} completion:^(id response) {
            [subscriber sendNext:@33];
            [subscriber sendCompleted];
        }];
        return nil;
    }];
}

上面fetchData1中sendNext了2次,fetchData3sendNext了1次,最终执行结果是(11,33),也就是fetchData3在sendNext:@33后,然后再sendCompleted后zipWith就完成了,不会去理会fetchData1还有sendNext:@22。因此,zip或者zipWith涉及到的信号劲量保持sendNext数目一致。

所以zipWith的作用就是控制2个信号一起返回,那么如果想要控制多个信号一起返回该怎么做呢,下面看一下join方法。

2.2 join

RACStream.m

+ (instancetype)join:(id<NSFastEnumeration>)streams block:(RACStream * (^)(id, id))block {
    RACStream *current = nil;

    // Creates streams of successively larger tuples by combining the input
    // streams one-by-one.
    for (RACStream *stream in streams) {
        // For the first stream, just wrap its values in a RACTuple. That way,
        // if only one stream is given, the result is still a stream of tuples.
        if (current == nil) {
            current = [stream map:^(id x) {
                return RACTuplePack(x);
            }];

            continue;
        }

        current = block(current, stream);
    }

    if (current == nil) return [self empty];

    return [current map:^(RACTuple *xs) {
        // Right now, each value is contained in its own tuple, sorta like:
        //
        // (((1), 2), 3)
        //
        // We need to unwrap all the layers and create a tuple out of the result.
        NSMutableArray *values = [[NSMutableArray alloc] init];

        while (xs != nil) {
            [values insertObject:xs.last ?: RACTupleNil.tupleNil atIndex:0];
            xs = (xs.count > 1 ? xs.first : nil);
        }

        return [RACTuple tupleWithObjectsFromArray:values];
    }];
}
  1. for循环部分,如果信号数组里只有1个信号,那么通过map,最终获取的结果被放进一个tuple里,比如[value1];如果数组里信号多余1个,那么第一个信号和第二信号就要做zipWith(此时block就是zipWith)操作,参考上面zipWith,得到的返回结果是[[value1],value2];如果还有第3个信号,那么将信号1和信号2 zipWith的结果与信号3继续zipWith,得到的结果就是[[[value1],value2], value3];
  2. return部分,for循环得到的信号最后sendNext的值是一个[[[value1],value2], value3]之类的tuple,如同注释里的"(((1), 2), 3)"一样,需要转换成[value1, value2, value3]。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容