iOS开发之RAC(二)进阶篇

初级篇简单的介绍了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的了解有多少,可以去美团看技术博客:传送门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • RAC使用测试Demo下载:github.com/FuWees/WPRACTestDemo 1.ReactiveC...
    FuWees阅读 6,207评论 3 10
  • 1.ReactiveCocoa简介 ReactiveCocoa(简称为RAC),是由Github开源的一个应用于i...
    清蘂翅膀的技术阅读 1,921评论 0 1
  • 前言由于时间的问题,暂且只更新这么多了,后续还会持续更新本文《最快让你上手ReactiveCocoa之进阶篇》,目...
    Karos_凯阅读 1,670评论 0 6
  • 前言 之前对RAC有了一个基本的认识,了解了它的作用,以及RAC的运行机制,我们知道只要是信号(RACSignal...
    大大盆子阅读 4,455评论 0 11
  • 堂屋摆钟上方的墙里 钉着一张黑白照片 每次上楼时 我都会和她招招手 年轻的CAD女老师 和一个男人 盘踞在自己正对...
    唐晟艺阅读 212评论 0 1