美文网首页iOS OCReactiveObjCRAC
iOS开发之RAC(二)进阶篇

iOS开发之RAC(二)进阶篇

作者: 赤小豆nil | 来源:发表于2018-01-16 23:32 被阅读703次

    初级篇简单的介绍了RAC。本文将介绍RAC在项目中常见的类和最常用的一些操作方法!!!

    一、常见类

    1、RACSiganl :信号类。

    • RACEmptySignal :空信号,用来实现 RACSignal 的 +empty 方法;
    • RACReturnSignal :一元信号,用来实现 RACSignal 的 +return: 方法;
    • RACDynamicSignal :动态信号,使用一个 block - 来实现订阅行为,我们在使用 RACSignal 的 +createSignal: 方法时创建的就是该类的实例;
    • RACErrorSignal :错误信号,用来实现 RACSignal 的 +error: 方法;
    • RACChannelTerminal :通道终端,代表 RACChannel 的一个终端,用来实现双向绑定。

    2、RACSubscriber :订阅者

    3、RACDisposable :用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。

    • RACSerialDisposable :作为 disposable 的容器使用,可以包含一个 disposable 对象,并且允许将这个 disposable 对象通过原子操作交换出来;
    • RACKVOTrampoline :代表一次 KVO 观察,并且可以用来停止观察;
    • RACCompoundDisposable :它可以包含多个 disposable 对象,并且支持手动添加和移除 disposable 对象
    • RACScopedDisposable :当它被 dealloc 的时候调用本身的 -dispose 方法。

    4、RACSubject :信号提供者,自己可以充当信号,又能发送信号。

    • RACGroupedSignal :分组信号,用来实现 RACSignal 的分组功能;
    • RACBehaviorSubject :重演最后值的信号,当被订阅时,会向订阅者发送它最后接收到的值;
    • RACReplaySubject :重演信号,保存发送过的值,当被订阅时,会向订阅者重新发送这些值。

    5、RACTuple :元组类,类似NSArray,用来包装值.

    6、RACSequence: RAC中的集合类

    7、RACCommand: RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。

    8、RACMulticastConnection :用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。

    9、RACScheduler: RAC中的队列,用GCD封装的。

    • RACImmediateScheduler :立即执行调度的任务,这是唯一一个支持同步执行的调度器;
    • RACQueueScheduler :一个抽象的队列调度器,在一个 GCD 串行列队中异步调度所有任务;
    • RACTargetQueueScheduler :继承自 RACQueueScheduler ,在一个以一个任意的 GCD 队列为 target 的串行队列中异步调度所有任务;
    • RACSubscriptionScheduler :一个只用来调度订阅的调度器。


      以上摘录自:
      iOS ReactiveCocoa 最全常用API整理(可做为手册查询)


    二、常见操作

    1、 combineLatest:把多个信号合并成一个信号,而且需要每个信号都sendNext一次,才会触发这个合并的信号!!

        UITextField *nameTextField = [[UITextField alloc]initWithFrame:CGRectMake(20, 100, 300, 40)];
        nameTextField.borderStyle = UITextBorderStyleRoundedRect;
        [self.view addSubview:nameTextField];
        
        UITextField *pwdTextField = [[UITextField alloc]initWithFrame:CGRectMake(20, 140, 300, 40)];
        pwdTextField.borderStyle = UITextBorderStyleRoundedRect;
        [self.view addSubview:pwdTextField];
        
        UIButton *addBtn = [UIButton buttonWithType:UIButtonTypeContactAdd];
        addBtn.center = self.view.center;
        [self.view addSubview:addBtn];
        RACSignal *signalBtn = [addBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
    
    //    combineLatest 把多个信号捆綁成一个信号,最多不要超过五个
    //    RACTuple 元组,可以像是一个字典,里面可以放多中类型
        [[RACSignal combineLatest:@[nameTextField.rac_textSignal, pwdTextField.rac_textSignal ,signalBtn]] subscribeNext:^(RACTuple * _Nullable x) {
            NSLog(@"%@",x);
      
        }];
    

    上面监听了两个文本框,一个按钮。如果不触发按钮的点击事件,那么NSLog(@"%@",x);将永远无法打印出来,上面说了必须每个信号都sendNext一次,才会触发这个合并的信号!!

    因为是多个信号合并出一个信号,返回的值里就包含多个信号的值。取值:

        [[RACSignal combineLatest:@[nameTextField.rac_textSignal, pwdTextField.rac_textSignal ,signalBtn]] subscribeNext:^(RACTuple * _Nullable x) {
            NSString *name = x.first;
            NSString *pwd = x.second;
            UIButton *btn = x.third;
            NSLog(@"%@ %@ %@",name,pwd,btn);
        }];
    

    顺序是按你放进去的信号顺序排序的,返回值RACTuple叫元组,可以自己点进去看看这个东西,不多做解释了!!


    2、reduce:把元祖里的值分别都取出来,然后对这些值做一些操作,再合成一个值返回出去。

    image.png

    上面这段代码,可用于登录时的操作,监听两个文本框的值,如果都有值,返回的BOOL值可以给按钮,让按钮变成可点击状态。如果不满足条件,则不可点击!!!


    3、 map:映射,取到监听后的值,映射成一个新值,返回出去!!

        [[[nameTextField rac_textSignal] map:^id _Nullable(NSString * _Nullable value) {
            
            return @(value.length > 0);
            
        }] subscribeNext:^(id  _Nullable x) {
            
            pwdTextField.hidden = [x boolValue];
            
        }];
    

    开发中,如果信号发出的值不是信号,映射一般使用Map


    4、 flattenMap:映射,取到信号源的值,映射成一个新的信号,返回出去!!

        [[[nameTextField rac_textSignal] flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
            
            value = [NSString stringWithFormat:@"数据处理: %@",value];
            
            return [RACReturnSignal return:value];
            
        }] subscribeNext:^(id  _Nullable x) {
            
            NSLog(@"%@",x);
            
        }];
    

    开发中,如果信号发出的值是信号,映射就使用FlatternMap


    5、merge:捆绑法,不分先后。

        [[[nameTextField rac_textSignal] merge:[pwdTextField rac_textSignal]] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    把多个信号捆绑在一起,任何一个信号有新值都会触发这个捆绑的信号!谁触发的信号,就拿到谁的值!!


    6、take:从开始一共取N次的信号

        RACSignal *signalBtn = [addBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
        [[signalBtn take:3] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    你点击按钮的前三次,都会有打印,第四次开始就不会再打印了。


    7、takeLast:取最后N次的信号,前提条件,订阅者必须调用完成,因为只有完成,就知道总共有多少信号,然后取最后N次的信号!!

        RACSubject * subject = [RACSubject subject];
        [[subject takeLast:1] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        [subject sendNext:@"one"];
        [subject sendNext:@"two"];
        [subject sendNext:@"three"];
        [subject sendCompleted];
    

    只会打印three,取的是最后一次的信号!!!
    RACSubject既可以作为信号,又可以发送信号!!!


    8、filter:过滤信号,return 满足条件的信号。

        [[[nameTextField rac_textSignal] filter:^BOOL(NSString * _Nullable value) {
            return [value integerValue] % 2 == 0;
        }] subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    输入框内只有你输入满足条件的数字才会被打印出来!!!


    9、ignore:忽略信号,忽略掉你规定的值。

        [[[nameTextField rac_textSignal] ignore:@"3"] subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    输入框出现“3”将不被打印!!!内部还是调用filter来实现的。


    10、distinctUntilChanged:监听的值有明显变化时,才会发出信号。

        [[[nameTextField rac_textSignal] distinctUntilChanged] subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    UI刷新的时候,用的最多。


    11、concat:按顺序拼接信号,按顺序接收信号,但是必须等上一个信号完成,下一个信号才有用!!!

        [[[nameTextField rac_textSignal] concat:[pwdTextField rac_textSignal]] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
            //永远不会打印,有关pwdTextField的值,因为前面的信号,没有调用sendCompleted。
        }];
    

    下面正确使用

        RACSignal *signalOne = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"下载"];
            [subscriber sendCompleted];
            return nil;
        }];
        RACSignal *signalTwo = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"解压"];
            return nil;
        }];
        [[signalOne concat:signalTwo] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
            //先打印下载,后打印解压
        }];
    

    可用于两个网络请求的数据之间有依赖关系!!!


    12、then:也是拼接信号,一样需要前面的信号调用sendCompleted,才会发送信号。

        RACSignal *signalOne = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"下载"];
            [subscriber sendCompleted];
            return nil;
        }];
        RACSignal *signalTwo = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"解压"];
            return nil;
        }];
       
        [[signalOne then:^RACSignal * _Nonnull{
            return signalTwo;
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    然而他永远不会打印“下载”,只会打印拼接在最后的一个信号!!!


    13、zipWith:把两个信号压缩成一个信号,且必须两个信号都触发了(同次数),才会打印。两个信号的值,会合并成一个元组。

        RACSignal *signalBtn = [addBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
        [[[nameTextField rac_textSignal] zipWith:signalBtn] subscribeNext:^(RACTwoTuple<NSString *,id> * _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    输入框触发了三次后,点击一下按钮,就会打印出输入框第一次触发的值和按钮的值!!当按钮点击四次后,不会有打印。必须再次触发输入框的信号,就会打印出,输入框第四次触发信号的值和第四次按钮时的各种状态。(说不清,自己去试一下吧)!!!


    14、takeUntil:直到某个信号触发,不再监听。

        [[[nameTextField rac_textSignal] takeUntil:signalBtn] subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    点击按钮以后,就再也不会打印了!!!


    15、skip:跳过前面N次信号,和take相反。

        RACSignal *signalBtn = [addBtn rac_signalForControlEvents:UIControlEventTouchUpInside];
        [[signalBtn skip:3] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    按钮前三次点击无打印,第四次点击以后,都会打印!!!


    16、switchToLatest:用来接收信号发出的信号。

        RACSubject *Asignal = [RACSubject subject];
        RACSubject *Bsignal = [RACSubject subject];
        
        [Asignal.switchToLatest subscribeNext:^(id x) {
            //打印出 1
            NSLog(@"%@",x);
        }];
        //A发送B
        [Asignal sendNext:Bsignal];
        //B发送 1
        [Bsignal sendNext:@1];
    

    只能用于信号发出的信号


    17、doNext和doCompleted和doError:在发送信号前。

        [[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"chixiaodou"];
            [subscriber sendCompleted];
            return nil;
        }] doNext:^(id  _Nullable x) {
            //执行[subscriber sendNext:@"chixiaodou"]前会先走这个block
            NSLog(@"doNext");
        }] doCompleted:^{
            //执行[subscriber sendCompleted]前会先走这个block
            NSLog(@"doCompleted");
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    doError的栗子是一样的,就不举了。


    18、interval:多少秒执行一次。

        [[RACSignal interval:1 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    每秒都会打印现在的时间,要记得调用takeUntil来结束它,不然一直打印,直到天荒地老...

        [[[RACSignal interval:1 onScheduler:[RACScheduler scheduler]] takeUntil:self.rac_willDeallocSignal] subscribeNext:^(NSDate * _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    控制器消失后就不再打印了


    19、delay:延迟多少秒执行。

        [[signalBtn delay:2] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    点击按钮后,过2秒才打印


    20、retry:重试,直到成功。

        [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            static int a = 1;
            if (a > 3) {
                [subscriber sendNext:@(a)];
            }else{
                [subscriber sendError:nil];
            }
            a++;
            return nil;
            
        }] retry] subscribeNext:^(id x) {
            NSLog(@"%@",x);
        } error:^(NSError *error) {
            
        }];
    

    直到a=4才会打印出来


    21、replay:被多次订阅后,每次都是重新执行。

        __block int a = 10;
        RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            a += 5;
            [subscriber sendNext:@(a)];
            return nil;
        }]replay];
        
        [signal subscribeNext:^(id x) {
            NSLog(@"第一个订阅者%@",x);
        }];
        
        [signal subscribeNext:^(id x) {
            NSLog(@"第二个订阅者%@",x);
        }];
        
        [signal subscribeNext:^(id x) {
            NSLog(@"第三个订阅者%@",x);
        }];
    

    被连续三次订阅,打印出来的值都是15,说明后面的每次订阅a=10!!!如果去除replay,将会打印出15、20、25!!加上replay后,后面的订阅者都是从10开始算的!!!


    22、throttle:节流,某个时间内只获取最后一次的信号。

        [[signalBtn throttle:3] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    

    如果一秒点了一百下按钮(单身的手速),那么再过三秒,只会打印出你第一百下点击按钮时的信号,前面的99次都不接收!!!从你不点击按钮后算三秒!!!


    23、RACSequence:RAC里的集合类

        NSArray * arr = @[@(1), @(2), @(3), @(4), @(5)] ;
        RACSequence * s1 = [arr rac_sequence];
        arr = [s1 array];
    

    可以和数组之间任意转换


    24、RACChannelTerminal:用来实现双向绑定的类

        RACChannelTerminal * signalnameText = [nameTextField rac_newTextChannel];
        RACChannelTerminal * signalpwdText = [pwdTextField rac_newTextChannel];
        [signalnameText subscribe:signalpwdText];
        [signalpwdText subscribe:signalnameText];
    

    不管怎么输入,两个输入框里的内容已经完全保持一致了!!!


    25、RAC(TARGET, ...):获取某个信号的值赋给属性

        RAC(self.person,name) = nameTextField.rac_textSignal;
    

    输入框里的值不停的变化,那么person类的name属性值,也会不停的变化!!!


    26、RACObserve(TARGET, KEYPATH):监听某个类的某个属性

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

    属性的值一但改变就会打印出来!!!


    上面列举的基本上是比较常用的,RAC还有非常多的功能需要自己去挖掘了,而且这些功能可以任意的组合,针对不同的业务逻辑,使用不同的组合方式。因为RAC也用了链式编程思想,非常之灵活。所以说RAC的学习曲线异常的陡峭!

    如果对block不太懂的,在学习RAC之前建议了解一下block

    注意:使用RAC时,牢记信号三部曲,顺序不能乱!!!

    美团是我知道使用RAC最多的APP,想要知道美团对RAC的了解有多少,可以去美团看技术博客:传送门

    相关文章

      网友评论

      • 这面包有毒:combineLatest是任何一个被订阅的对象发出一个序列都会触发。并且发送每个合并序列最新的值,zip才是合并信号都send才触发,就只不造RAC有木有zip这个操作符

      本文标题:iOS开发之RAC(二)进阶篇

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