美文网首页iOS精选racReactiveCocoa
ReactiveCocoa学习笔记整理(一)

ReactiveCocoa学习笔记整理(一)

作者: 爱敲代码的果果 | 来源:发表于2017-11-20 16:52 被阅读140次

    由于近期时间相对宽裕,以及很多朋友询问关于RAC的学习路径以及资料,故而整理一下之前自己学习RAC的学习笔记,供大家查阅练习。该文章只讲解了RAC的详细用法,有关其内部原理以及实现,请参考ReactiveCocoa深入理解

    一. ReactiveCocoa简介

    1.解决的问题

    作为一个iOS开发者,你写的每一行代码几乎都是在相应某个事件,例如按钮的点击,收到网络消息,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation)。但是这些事件都用不同的方式来处理,比如action、delegate、KVO、callback等。ReactiveCocoa为事件定义了一个标准接口,从而可以使用一些基本工具来更容易的连接、过滤和组合。

    2.编程思想

    ReactiveCocoa结合了几种编程风格:

    • 函数式编程(Functional Programming)

    • 响应式编程(Reactive Programming)

    所以,你可能听说过ReactiveCocoa被描述为函数响应式编程(FRP)框架。以后使用RAC解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。

    3.常见的五个宏

    • RAC(TARGET, [KEYPATH, [NIL_VALUE]])

    作用: 用于给某个对象的某个属性绑定

    实例: 只要文本框的文字改变,就会修改label的文字

    RAC(self.labelView,text) = _textField.rac_textSignal;

    • RACObserve(self, name)

    作用: 监听某个对象的某个属性,返回的是信号

    实例: 监听self.view的center变化

    [RACObserve(self.view, center) subscribeNext:^(id x) {
    NSLog(@"%@",x);
    }];

    注意事项: 当RACObserve放在block里面使用时一定要加上weakify,不管里面有没有使用到self;否则会内存泄漏,因为RACObserve宏里面就有一个self。

    @weakify(self);
    RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) {
       Avoids a retain cycle because of RACObserve implicitly referencing self
      @strongify(self);
      return RACObserve(arrayController, items);
    }];
    
    • @weakify(Obj)和@strongify(Obj)

    一般两个都是配套使用,在主头文件(ReactiveCocoa.h)中并没有导入,需要自己手动导入,RACEXTScope.h才可以使用。但是每次导入都非常麻烦,只需要在主头文件自己导入就好了

    • RACTuplePack

    作用: 把数据包装成RACTuple(元组类)

    实例: 把参数中的数据包装成元组

    RACTuple *tuple = RACTuplePack(@10,@20);

    • RACTupleUnpack

    作用: 把RACTuple(元组类)解包成对应的数据

    实例: 把参数中的数据包装成元组

    RACTuple *tuple = RACTuplePack(@"xmg",@20);

    注意事项: 解包元组,会把元组的值,按顺序给参数里面的变量赋值

    二. RACSignal基础知识点

    1.信号类(RACSignal) 简介

    信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出.默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。

    如何订阅信号?

    调用信号RACSignal的subscribeNext就能订阅

    2.信号类(RACSignal)实践

    1. RACSignal的简单使用

    完整的创建RACSignal 包含三部分sendError(不一定要有) sendNext(可多个) sendCompleted(不一定要有)。 下面代码中的RACSubscriber表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。

     RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            @strongify(self)
            NSError *error;
            if ([self.curTag isEqualToString:@"error"]) {
                error=[[NSError alloc]initWithDomain:@"myError" code:2001 userInfo:nil];
                [subscriber sendError:error];
            }
            else
            {
                [subscriber sendNext:@"1"];
                [subscriber sendNext:@"3"];
                [subscriber sendNext:@"5"];
                [subscriber sendCompleted];
            }
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"执行清理");
                //RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它
                //使用场景:不想监听某个信号时,可以通过它主动取消订阅信号
            }];
        }];
        
        [signal subscribeNext:^(id x) {
            NSLog(@"当前的值为:%@",x);
        }];
        
        [signal subscribeError:^(NSError *error) {
            NSLog(@"当前出现错误%@",error);
        }];
        
        [signal subscribeNext:^(id x) {
            NSLog(@"2当前的值为:%@",x);
        }];
    

    以上代码的输出为:

        执行清理
        当前出现错误Error Domain=myError Code=2001 "(null)"
        执行清理
        执行清理
    

    2. filter、map以及flattenMap的使用

    首先我们创建简单的signal对象,然后通过该对象对filter、map以及flattenMap进行讲解。

     RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"1"];
            [subscriber sendNext:@"3"];
            [subscriber sendNext:@"15"];
            [subscriber sendNext:@"wujy"];
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"执行清理");
            }];
        }];
    

    然后,我们看一下filter的作用,filter顾名思义就是过滤的意思,我们来看以下代码:

     [[signal filter:^BOOL(id value) {
            if ([value isEqualToString:@"wujy"]) {
                return YES;
            }
            return NO;
        }] subscribeNext:^(id x) {
            NSLog(@"当前的值为:%@",x);
        }];
    //输出为: 当前的值为:wujy  执行清理
    

    以上的代码逻辑中,对signal进行过滤,只有传入的字符串为“wujy”,才发送信号。

    接下来,我们看一下先filter过滤后又用map进行转换的效果,请看以下代码:

    [[[signal filter:^BOOL(id value) {
            if ([value isEqualToString:@"wujy"]) {
                return NO;
            }
            return YES;
        }] map:^id(NSString *value) {
            return @(value.length);
        }] subscribeNext:^(NSNumber *x) {
            NSLog(@"当前的位数为:%zd",[x integerValue]);
        }];
    //输出为: 当前的位数为:1  
             当前的位数为:1     
             当前的位数为:2    
             执行清理
    

    用map后原来的值就被转化,上述代码中,将value的值转化成了长度。

    接下来,我们再看一下flattenMap的使用效果,代码如下:

     [[signal flattenMap:^RACStream *(id value) {
            return [RACSignal return:[NSString stringWithFormat:@"当前输出为:%@",value]];
        }] subscribeNext:^(id x) {
            NSLog(@"flattenMap中执行:%@",x);
        }];
    flattenMap的作用是 根据前一个信号的参数创建一个新的信号,以上代码的输出为:  
    flattenMap中执行:当前输出为:1
    flattenMap中执行:当前输出为:3
    flattenMap中执行:当前输出为:15
    flattenMap中执行:当前输出为:wujy
    

    那么map跟flattenMap有什么区别呢?

    1. FlatternMap中的Block返回信号
    2. Map中的Block返回对象
    3. 如果信号发出的值不是信号,映射一般使用Map
    4. 如果信号发出的值是信号,映射一般使用FlatternMap

    3. ignore、ignoreValues、take、takeUntilBlock以及takeLast的使用

    之后,我们再继续介绍RAC的其他基本知识点,同样的方式,我们通过代码的方式呈现。
    首先,看一下ignore的使用,该用法相对简单,我就直接上代码了。

     //ignore 忽略某个值
        [[signal ignore:@"3"] subscribeNext:^(id x) {
            NSLog(@"当前的值为:%@",x);
        }];
        //输出:当前的值为:1  当前的值为:15  当前的值为:wujy   执行清理
    

    接下来,我们看一下ignoreValues的使用效果,ignoreValues 这个比较极端,忽略所有值,只关心Signal结束,也就是只取Comletion和Error两个消息,中间所有值都丢弃

    [[signal ignoreValues] subscribeNext:^(id x) {
            //它是没机会执行  因为ignoreValues已经忽略所有的next值
            NSLog(@"ignoreValues当前值:%@",x);
        } error:^(NSError *error) {
            NSLog(@"ignoreValues error");
        } completed:^{
            NSLog(@"ignoreValues completed");
        }];
    //    输出
    //    ignoreValues completed
    

    然后,我们看一下take的相关用法。首先,我们先登场的是take的简单使用,take的意思是:从开始一共取N次的信号

    [[signal take:1] subscribeNext:^(id x) {
            NSLog(@"take 获取的值:%@",x);
        }];
        //输出:take 获取的值:1
    

    用法相对简单,不做过多介绍,我们马上来看一下takeUntilBlock的用法,takeUntilBlock的意思是:对于每个next值,运行block,当block返回YES时停止取值

     [[signal takeUntilBlock:^BOOL(NSString *x) {
            if ([x isEqualToString:@"15"]) {
                return YES;
            }
            return NO;
        }] subscribeNext:^(id x) {
            NSLog(@"takeUntilBlock 获取的值:%@",x);
        }];
    //    输出
    //    takeUntilBlock 获取的值:1
    //    takeUntilBlock 获取的值:3
    

    最后,我们看一下takeLast的使用,takeLast的意思是: 取最后N次的信号,但是它有一个前提条件,订阅者必须调用完成,因为只有完成,才知道总共有多少信号。

      [[signal takeLast:1] subscribeNext:^(id x) {
            NSLog(@"takeLast 获取的值:%@",x);
        }];
        //输出:takeLast 获取的值:wujy
    

    4. skip、skipUntilBlock、skipWhileBlock、startWith以及reduceEach的使用

    RAC的基础知识真是众多且难记,喝杯小茶,我们接着来。啦啦啦,我们再看一下关于剩余的知识点。首先,skip登场,skip的字面意思即跳跃,他的使用就是跳过几个信号,不接受。skipUntilBlock的意思类似,skipUntilBlock是一直跳,直到block为YES。

    //skip
    [[signal skip:2] subscribeNext:^(id x) {
            NSLog(@"skip 获取的值:%@",x);
        }];
        //输出:skip 获取的值:15    skip 获取的值:wujy
    
    //skipUntilBlock
     [[signal skipUntilBlock:^BOOL(NSString *x) {
            if ([x isEqualToString:@"15"]) {
                return YES;
            }
            return NO;
        }] subscribeNext:^(id x) {
            NSLog(@"skipUntilBlock 获取的值:%@",x);
        }];
    //    输出
    //    skipUntilBlock 获取的值:15
    //    skipUntilBlock 获取的值:wujy
    
    

    skipWhileBlock跟skipUntilBlock是相反的意思,skipWhileBlock的意思是一直跳,直到block为NO

    [[signal skipWhileBlock:^BOOL(NSString *x) {
            if ([x isEqualToString:@"15"]) {
                return NO;
            }
            return YES;
        }] subscribeNext:^(id x) {
            NSLog(@"skipWhileBlock 获取的值:%@",x);
        }];
    //    输出
    //    skipWhileBlock 获取的值:15
    //    skipWhileBlock 获取的值:wujy
    

    接下来,我们看一下startWith以及reduceEach的用法,startWith表示起始位置增加相应的元素,不要跟字符串的拼接混用,跟字符串的拼接还是有区别的。

     RACSignal *addStartSignal=[RACSignal return:@"123"];
        [[addStartSignal startWith:@"345"] subscribeNext:^(id x) {
            NSLog(@"startWith增加的值操作 %@",x);
        }];
    //    输出
    //    startWith增加的值操作 345
    //    startWith增加的值操作 123
    

    reduceEach的意思是聚合,用于信号发出的内容是元组,把信号发出元组的值聚合成一个值。

     RACSignal *aSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:RACTuplePack(@1,@4)];
            [subscriber sendNext:RACTuplePack(@2,@3)];
            [subscriber sendNext:RACTuplePack(@5,@2)];
            return nil;
        }];
        
        [[aSignal reduceEach:^id(NSNumber *first,NSNumber *secnod){
            return @([first integerValue]+[secnod integerValue]);
        }] subscribeNext:^(NSNumber *x) {
            NSLog(@"reduceEach当前的值:%zd",[x integerValue]);
        }];
    //    输出
    //    reduceEach当前的值:5
    //    reduceEach当前的值:5
    //    reduceEach当前的值:7
    

    5. 关于时间以及流程相关的RAC语法操作

    说到时间,首先想到的就是计时器的使用场景,在RAC中也有对应的计时器操作API,即: (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler的用法。模仿定时器用,我们设置take方式,每隔段时间发出一个信号。

     [[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] take:5]subscribeNext:^(id x) {
            NSLog(@"interval-take :吃药");
        }];
        //    输出(每隔一秒执行一句)
        //    interval-take :吃药
        //    interval-take :吃药
        //    interval-take :吃药
        //    interval-take :吃药
        //    interval-take :吃药
    

    接下来,我们看一下有关超时的操作流程,请看以下代码:

     [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
            [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
                NSLog(@"我快到了");
                [subscriber sendNext:nil];
                [subscriber sendCompleted];
                return nil;
                //延迟2秒后执行next事件
            }] delay:2] subscribeNext:^(id x) {
                NSLog(@"我到了");
                [subscriber sendNext:nil];
                [subscriber sendCompleted];
            }];
            return nil;
        }] timeout:1 onScheduler:[RACScheduler mainThreadScheduler]] subscribeError:^(NSError *error) {
            NSLog(@"你再不来,我走了");
        }];
        
        //输出
        //我快到了
        //你再不来,我走了
        //我到了
    

    在我们的实际开发中,有很多场景需要我们重新执行某个操作,比如网络请求中的再次刷新等,类似的场景我们就可以使用retry语法来实现。若发送的是error则可以使用retry来尝试重新刺激信号 retry重试 :只要失败,就会重新执行创建信号中的block,直到成功.

     [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
            NSLog(@"i = %d",i);
            if (i == 5) {
                [subscriber sendNext:@"i == 2"];
            }else{
                i ++;
                [subscriber sendError:nil];
            }
            return nil;
            //当发送的是error时可以retry重新执行
        }] retry] subscribeNext:^(id x) {
            NSLog(@"%@",x);
        }];
        
        //    输出:
        //    i = 0
        //    i = 1
        //    i = 2
        //    i = 3
        //    i = 4
        //    i = 5
        //    i == 2
    

    还有一种我们常用的场景,即我们需要达到某个条件时,停止某些操作。如通知的注销,计时器的销毁等,我们可以使用takeUntil更加方便快捷的实现该需求。

        //创建一个信号
        [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
            //创建一个定时信号,每隔1秒刺激一次信号
            [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
                [subscriber sendNext:@"直到世界的尽头才能把我们分开"];
            }];
            return nil;
            //直到此情况下停止刺激信号
        }] takeUntil:[RACSignal createSignal:^RACDisposable *(id subscriber) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"世界的尽头到了");
                [subscriber sendNext:@"世界的尽头到了"];
            });
            return nil;
        }]] subscribeNext:^(id x) {
            NSLog(@"%@", x);
        }];
        //输出:
        //    直到世界的尽头才能把我们分开
        //    直到世界的尽头才能把我们分开
        //    直到世界的尽头才能把我们分开
        //    世界的尽头到了
    

    最后,再介绍一下doNext跟doCompleted以及throttle的使用,doNext: 执行Next之前,会先执行这个Block; doCompleted: 执行sendCompleted之前,会先执行这个Block。先看一下doNext跟doCompleted的使用:

    [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"执行sendNext"];
            NSLog(@"执行sendNext");
            [subscriber sendCompleted];
            return nil;
        }] doNext:^(id x) {
            NSLog(@"执行doNext");
        }] doCompleted:^{
            NSLog(@"执行doCompleted");
        }] subscribeNext:^(id x) {
            NSLog(@"执行subscribeNext");
        }];
        
    //    输出
    //    执行doNext
    //    执行subscribeNext
    //    执行sendNext
    //    执行doCompleted
    

    throttle是节流的意思,用来处理当某个信号发送比较频繁的情况。该情况下可以使用节流,在某一段时间不发送信号内容,过了一段时间获取信号的最新内容发出。

        RACSubject *throttleSignal = [RACSubject subject];
        [throttleSignal sendNext:@"throttle a"];
        // 节流,在一定时间(4秒)内,不接收任何信号内容,过了这个时间(1秒)获取最后发送的信号内容发出。
        [[throttleSignal throttle:4] subscribeNext:^(id x) {
            NSLog(@"throttleSignal:%@",x);
        }];
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"世界的尽头到了");
             [throttleSignal sendNext:@"throttle b"];
             [throttleSignal sendNext:@"throttle c"];
        });
        //输出:throttleSignal:throttle c
    

    6. combineLatest、reduce、then、aggregateWithStart、scanWithStart的使用

    在此之前,我们大多介绍的是基于单个信号的操作,接下来,我们讲解一下关于信号与信号之间的操作与处理问题。我们需要创建两个简单的信号,然后基于这两个信号,我们进行接下来的讲解工作。

     RACSignal *aSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"1"];
            [subscriber sendNext:@"3"];
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"aSignal清理了");
            }];
        }];
        
        RACSignal *bSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"7"];
            [subscriber sendNext:@"9"];
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"bSignal清理了");
            }];
        }];
    

    首先,看一下combineLatest的用法,他的作用是将多个信号合并起来,并且拿到各个信号的最新的值, combineLatest有一个前提条件,即:必须每个合并的signal至少都有过一次sendNext,才会触发合并的信号。

        RACSignal *combineSignal = [aSignal combineLatestWith:bSignal];
        
        [combineSignal subscribeNext:^(id x) {
            
            NSLog(@"combineSignal为:%@",x);
        }];
        //输出
    //    combineSignal为:<RACTuple: 0x600000015c30> (
    //                                               3,
    //                                               7
    //                                               )
    //    combineSignal为:<RACTuple: 0x600000015cb0> (
    //                                                3,
    //                                                9
    //                                                                                                  )
    

    combineLatest一般结合reduce聚合一起使用,将产生的最新的值聚合在一起,并生成一个新的信号。

     RACSignal *combineReduceSignal=[RACSignal combineLatest:@[aSignal,bSignal] reduce:^id(NSString *aItem,NSString *bItem){
            return [NSString stringWithFormat:@"%@-%@",aItem,bItem];
        }];
        
        [combineReduceSignal subscribeNext:^(id x) {
            NSLog(@"合并后combineSignal的值:%@",x);
        }];
        //输出:aSignal清理了   合并后combineSignal的值:3-7    合并后combineSignal的值:3-9   bSignal清理了
    

    从结果可以看出此种合并会将第一个信号中最后一个sendnext与后面信号的所有sendnext结合起来作为一个数组,而next触发次数以bSignal中的next次数为主。

    然后,介绍一下then的用法,用于连接两个信号,当第一个信号完成,才会连接then返回的信号。

      RACSignal *thenSignal=[aSignal then:^RACSignal *{
            return bSignal;
        }];
        
        [thenSignal subscribeNext:^(id x) {
            NSLog(@"thenSignal的值:%@",x);
        }];
        //输出  thenSignal的值:7   thenSignal的值:9   bSignal清理了  aSignal清理了
    

    then可以用来处理串行的需求,就像一下实例:

     [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"第一步");
            [subscriber sendCompleted];
            return nil;
        }] then:^RACSignal *{
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                NSLog(@"第二步");
                [subscriber sendCompleted];
                return nil;
            }];
        }] then:^RACSignal *{
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                NSLog(@"第三步");
                return nil;
            }];
        }] subscribeCompleted:^{
            NSLog(@"完成");
        }];
        //输出:第一步   第二步  第三步
    

    最后,我们看一下aggregateWithStart跟scanWithStart的使用,aggregateWithStart的意思是 从哪个位置开始 进行顺序两值进行操作 最后只有一个被操作后的值,而scanWithStart的意思是 从哪个位置开始 然后每个位置跟前面的值进行操作 它会有根据NEXT的个数来显示对应的值。我们通过具体的代码来看:

        RACSignal *operateSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@2];
            [subscriber sendNext:@12];
            [subscriber sendNext:@15];
            [subscriber sendCompleted];
            return nil;
        }];
        
        
        //aggregateWithStar
        [[operateSignal aggregateWithStart:@0 reduce:^id(NSNumber *running, NSNumber *next) {
            return @([running integerValue]+[next integerValue]);
        }] subscribeNext:^(id x) {
            NSLog(@"aggregateWithStart 当前值:%@",x);
        }];
        //输出
        //aggregateWithStart 当前值:29
        
        
        //scanWithStart 
        [[operateSignal scanWithStart:@0 reduce:^id(NSNumber *running, NSNumber *next) {
            return @([running integerValue]+[next integerValue]);
        }] subscribeNext:^(id x) {
            NSLog(@"scanWithStart 当前值:%@",x);
        }];
        //输出
        //scanWithStart 当前值:2
        //scanWithStart 当前值:14
        //scanWithStart 当前值:29
    

    7. 信号队列的使用

    信号队列顾名思义就是将一组信号排成队列,挨个调用。Talk is cheap, show you the code .

        //创建3个信号来模拟队列
        RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@"喜欢一个人"];
            [subscriber sendCompleted];
            return nil;
        }];
        RACSignal *signalC = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@"直接去表白"];
            [subscriber sendCompleted];
            return nil;
        }];
        RACSignal *signalD = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@"成功在一起"];
            [subscriber sendCompleted];
            return nil;
        }];
        
        RACSignal *signalGroup = [[signalB concat:signalC] concat:signalD];
        [signalGroup subscribeNext:^(id x) {
            NSLog(@"%@",x);
        }];
        
        //输出:喜欢一个人   直接去表白   成功在一起
    

    以上的代码中,我们使用了concat来连接组队列,concat将几个信号放进一个组里面,按顺序连接每个,每个信号必须执行sendCompleted方法后才能执行下一个信号。此处,我们也可以用merge来处理,merge用来合并队列。

     [[RACSignal merge:@[signalB,signalC,signalD]] subscribeNext:^(id x) {
            NSLog(@"merge:%@",x);
        }];
        //输出:merge:喜欢一个人   merge:直接去表白   merge:成功在一起
    

    concat跟merge的区别是什么呢?
    concat每个信号必须执行sendCompleted方法后才能执行下一个信号,而merge不用。

    最后的最后,我们看一下信号的压缩zipWith的用法,压缩具有一一对应关系,以2个信号中 消息发送数量少的为主对应。

    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@"我想你"];
            [subscriber sendNext:@"我不想你"];
            [subscriber sendNext:@"Test"];
            return nil;
        }];
        RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@"嗯"];
            [subscriber sendNext:@"你豁我"];
            return nil;
        }];
      
        [[signalA zipWith:signalB] subscribeNext:^(RACTuple* x) {
            //解包RACTuple中的对象
            RACTupleUnpack(NSString *stringA, NSString *stringB) = x;
            NSLog(@"%@%@", stringA, stringB);
        }];
        //输出:我想你  嗯   我不想你  你豁我
    

    若将此结果于合并作对比,我们可以发现他们只是触发next事件的次数所关联对象不一样,是以信号中next事件数量较少的为主。
    发现已经不知不觉介绍了很多了,那就先这样吧,剩下的内容我们下篇文章再聊,欢迎你前来围观。

    相关文章

      网友评论

      本文标题:ReactiveCocoa学习笔记整理(一)

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