2018/2/2 19:07:03当前位置推荐好文程序员浏览文章

关于RAC,一直想写一篇文章来总结和整理一下,可是一直没什么时间。年末,项目没那么忙,就抽空整理一下RAC的用法。本文参考了两篇文章,并添加整合了一些自己的东西进去,文章若有错误,欢迎指正。
参考文章:ReactiveCocoa进阶 , 最快让你上手ReactiveCocoa之基础篇

一、RAC介绍

RAC 是一个 iOS 中的函数式响应式编程框架,一般与MVVM配套使用。
在非RAC开发中,都是习惯赋值。在RAC开发中,需要改变开发思维,由赋值转变为绑定,并不需要重写set方法。
RAC项目的Podfile如下:

use_frameworks!target 工程名称 dopod ReactiveObjC, ~> 3.0.0end

如果使用Swift作为开发语言,建议使用RXSwift。关于RXSwift,以后会单独写一篇文章介绍。

RAC使用注意事项:
1.RAC学习曲线陡峭,团队开发时要谨慎使用,确保项目组所有成员都会RAC,再使用RAC构建项目。如果一个人开发一个项目,要使用RAC,先跟项目经理商量一下,不要自己想当然的想用就用,这样如果公司要加人,会增加招人成本,公司不一定乐意。
2.在使用过程中,尽量保持同一个项目的成员,代码风格一致,这样方便管理维护。
3.关于学习曲线陡峭,有的同学可能不以为然,随便写个demo,体验了几个RAC基本操作方法,就觉得自己已经会用RAC了。其实不然,要真正的用RAC构造整个项目,再配上MVVM,是需要一个较长学习周期的。

二、RAC常见用法

  • 代替代理
    rac_signalForSelector:用于替代代理
    // 需求:自定义redView,监听红色view中按钮点击    // 之前都是需要通过代理监听,给红色View添加一个代理属性,点击按钮的时候,通知代理做事情    // rac_signalForSelector:把调用某个对象的方法的信息转换成信号,就要调用这个方法,就会发送信号。    // 这里表示只要redV调用btnClick:,就会发出信号,订阅就好了。    [[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {        NSLog(@"点击红色按钮");    }];
  • 代替KVO
    rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变。
// 把监听redV的center属性改变转换成信号,只要值改变就会发送信号    // observer:可以传入nil    [[redV rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {        NSLog(@"%@",x);    }];
  • 监听事件 (代替addTarget)
    rac_signalForControlEvents:用于监听某个事件。
[[self.btn rac_signalForControlEvents:(UIControlEventTouchUpInside)]subscribeNext:^(__kindof UIControl  _Nullable x) {        NSLog(@"%@",x);    }];
  • 代替通知
    rac_addObserverForName:用于监听某个通知
// 把监听到的通知转换信号    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {        NSLog(@"键盘弹出");    }];
  • 监听文本框文字改变
    rac_textSignal:只要文本框发出改变就会发出这个信号
[_textField.rac_textSignal subscribeNext:^(id x) {       NSLog(@"文字改变了%@",x);   }];

三、常见类解释

RactiveCocoa中很重要的两个class,一个是RACSignal,一个是RACSequence,而这两个class的super class就是RACStream。
RACStream: 表示一个基本单元可以为任意值,其值会随着事件的变化而变化。可以在其上进行一些复杂的操作运算(map,filter,skip,take等.)此类不会被经常使用, 多情况下表现为signal和sequences(RACSignal 和RACSequence继承于RACStream类)。
RACSiganl:只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。signal能发送3种不同类型的事件:Next,Completed,Error。
信号三部曲:创建信号、订阅信号、发送信号。

// 1.创建信号    RACSignal siganl = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        // 2.发送信号,必须是一个对象类型        [subscriber sendNext:@1];        // 如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。        [subscriber sendCompleted];        //return nil;        return [RACDisposable disposableWithBlock:^{            // block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。            // 执行完Block后,当前信号就不在被订阅了。            NSLog(@"信号被销毁");        }];    }];    // 3.订阅信号,才会激活信号.    [siganl subscribeNext:^(id x) {        // block调用时刻:每当有信号发出数据,就会调用block.        NSLog(@"接收到数据:%@",x);    }];

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

// 1.创建信号    RACSubject subject = [RACSubject subject];    // 2.订阅信号    [subject subscribeNext:^(id x) {        // block调用时刻:当信号发出新值,就会调用.        NSLog(@"第一个订阅者%@",x);    }];    [subject subscribeNext:^(id x) {        // block调用时刻:当信号发出新值,就会调用.        NSLog(@"第二个订阅者%@",x);    }];    // 3.发送信号    [subject sendNext:@"1"];

RACReplaySubject:重复提供信号类,RACSubject的子类。
RACDisposable:用于取消订阅或者清理资源,在一个completed或者error事件之后,就会自动触发它,订阅会自动移除。也可以通过RACDisposable 手动移除订阅。

RACTuple:元组类,类似NSArray,用来包装值.
RACSequence:RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典,达到对值的一些过滤和转换。

// 遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)    NSDictionary dict = @{@"name":@"xmg",@"age":@18};    [dict.rac_sequence.signal subscribeNext:^(RACTuple x) {        // 解包元组,会把元组的值,按顺序给参数里面的变量赋值        RACTupleUnpack(NSString key,NSString value) = x;        // 相当于以下写法//        NSString key = x[0];//        NSString value = x[1];        NSLog(@"%@ %@",key,value);    }];

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

// 一、RACCommand使用步骤:    // 1.创建命令 initWithSignalBlock:(RACSignal  (^)(id input))signalBlock    // 2.在signalBlock中,创建RACSignal,并且作为signalBlock的返回值    // 3.执行命令 - (RACSignal )execute:(id)input        // 二、RACCommand使用注意:    // 1.signalBlock必须要返回一个信号,不能传nil.    // 2.如果不想要传递信号,直接创建空的信号[RACSignal empty];    // 3.RACCommand中信号如果数据传递完,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。    // 4.RACCommand需要被强引用,否则接收不到RACCommand中的信号,因此RACCommand中的信号是延迟发送的。        // 三、RACCommand设计思想:内部signalBlock为什么要返回一个信号,这个信号有什么用。    // 1.在RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求。    // 2.当RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了。        // 四、如何拿到RACCommand中返回信号发出的数据。    // 1.RACCommand有个执行信号源executionSignals,这个是signal of signals(信号的信号),意思是信号发出的数据是信号,不是普通的类型。    // 2.订阅executionSignals就能拿到RACCommand中返回的信号,然后订阅signalBlock返回的信号,就能获取发出的值。        // 五、监听当前命令是否正在执行executing        // 六、使用场景,监听按钮点击,网络请求
// 1.创建命令    RACCommcomm= [[RACCommalloc] initWithSignalBlock:^RACSignal (id input) {        NSLog(@"执行命令");        // 创建空信号,必须返回信号        //        return [RACSignal empty];        // 2.创建信号,用来传递数据        return [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {            [subscriber sendNext:@"请求数据"];            // 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。            [subscriber sendCompleted];            return nil;        }];    }];    // 强引用命令,不要被销毁,否则接收不到数据    _loginComm= command;// 3.订阅RACCommand中的信号    [command.executionSignals subscribeNext:^(id x) {        //订阅executionSignals就能拿到RACCommand中返回的信号,然后订阅signalBlock返回的信号,就能获取发出的值。        [x subscribeNext:^(id x) {            NSLog(@"%@",x);        }];    }];// RAC高级用法    // switchToLatest:用于signal of signals,获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号   / [command.executionSignals.switchToLatest subscribeNext:^(id x) {        NSLog(@"%@",x);    }];/// 4.监听命令是否执行完毕,默认会来一次,可以直接跳过,skip表示跳过第一次信号。    [[command.executing skip:1] subscribeNext:^(id x) {       // executing:判断当前的block是否在执行,执行完之后会返回NO        if ([x boolValue] == YES) {            // 正在执行            NSLog(@"正在执行");        }else{            // 执行完成            NSLog(@"执行完成");        }    }];   // 5.执行命令(控制器里执行此句代码)    [self.login_vm.loginCommexecute:nil];

RACMulticastConnection:用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。
使用注意:RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建。
RACScheduler:RAC中的队列,用GCD封装的。
RACUnit :表⽰stream不包含有意义的值,也就是看到这个,可以直接理解为nil.

四、RAC常用宏

1、RAC 绑定一个信号
RAC宏允许直接把信号的输出应用到对象的属性上 每次信号产生一个next事件,传递过来的值都会应用到该属性上

//1、RAC 把一个对象的摸个属性绑定一个信号,只有发出信号,就会吧信号的内容给对象的属性赋值。    //这里吧label的text属性绑定到textField改变信号中,textfield的内容发生改变的时候就会发出信号,只要文本框文字改变,就会修改label的文字。    RAC(self.label,text) = _textfield.rac_textSignal;

2、RACObserve 相当于kvo使用

//person为一个模型,只要模型里面的name字段改变,就会把最新的name值显示在label上[RACObserve(self.person, name) subscribeNext:^(id  _Nullable x) {        NSLog(@"%@",x);        self.nameLabel.text = x;    }];

3、@weakify 和@strongify 解决循环引用,注意这两个宏是配套使用才有效。

@weakify(self);    [[self.testTextFileld rac_textSignal] subscribeNext:^(NSString  _Nullable x) {        NSLog(@"%@",x);        @strongify(self);        self.label.text = x;    }];

4、 RACChannelTo 用于双向绑定
也可以用两个信号互相订阅(subscribe:)

5、RACTuplePack:把数据包装成RACTuple(元组类)

// 把参数中的数据包装成元组    RACTuple tuple = RACTuplePack(@(1),@(30));

6、RACTupleUnpack:把RACTuple(元组类)解包成对应的数据。

// 把参数中的数据包装成元组    RACTuple tuple = RACTuplePack(@"xmg",@20);    // 解包元组,会把元组的值,按顺序给参数里面的变量赋值    // name = @"xmg" age = @20    RACTupleUnpack(NSString name,NSNumber age) = tuple;

五、常见操作方法

所有的信号(RACSignal)都可以进行操作处理,因为所有操作方法都定义在RACStream.h中,因此只要继承RACStream就有了操作处理方法。

1. bind(绑定) 核心方法

ReactiveCocoa 操作的核心方法是 bind(绑定),而且也是RAC中核心开发方式。之前的开发方式是赋值,而用RAC开发,应该把重心放在绑定,也就是可以在创建一个对象的时候,就绑定好以后想要做的事情,而不是等赋值之后在去做事情。
RAC底层都是调用bind, 在开发中很少直接使用 bind 方法,bind属于RAC中的底层方法,我们只需要调用封装好的方法,bind用作了解即可.

2. flattenMap

把源信号的内容映射成一个新的信号,信号可以是任意类型
使用步骤:
1.传入一个block,block类型是返回值RACStream,参数value
2.参数value就是源信号的内容,拿到源信号的内容做处理
3.包装成RACReturnSignal信号,返回出去

//监听文本框的内容改变,重新映射成一个新的信号[[_textField.rac_textSignal flattenMap:^RACStream (id value) {    // block调用时机:信号源发出的时候    // block作用:改变信号的内容    // 返回RACReturnSignal (需要导入RACReturnSignal.h)    return [RACReturnSignal return:[NSString stringWithFormat:@"信号内容:%@", value]];}] subscribeNext:^(id x) {    NSLog(@"%@", x);}];

3. Map

把源信号的值映射成一个新的值
如果map的是一个sequence,那么会遍历里面的么一个元素,遍历的每一个值,都会做相同的处理

//监听文本框的内容改变,映射成一个新值.[[_textField.rac_textSignal map:^id(id value) {   //把处理好的内容,直接返回就好了,不用包装成信号,返回的值,就是映射的值。    return [NSString stringWithFormat:@"信号内容: %@", value];    }] subscribeNext:^(id x) {    NSLog(@"%@", x);}];

FlatternMap 和 Map 的区别

FlatternMap 中的Block 返回信号。
Map 中的Block 返回对象。
开发中,如果信号发出的值 不是信号 ,映射一般使用 Map
如果信号发出的值 是信号,映射一般使用 FlatternMap。

信号中的信号(signalOfsignals)用flatternMap:

// 创建信号中的信号RACSubject signalOfsignals = [RACSubject subject];RACSubject signal = [RACSubject subject];[[signalOfsignals flattenMap:^RACStream (id value) { // 当signalOfsignals的signals发出信号才会调用    return value;}] subscribeNext:^(id x) {    // 只有signalOfsignals的signal发出信号才会调用,因为内部订阅了bindBlock中返回的信号,也就是flattenMap返回的信号。    // 也就是flattenMap返回的信号发出内容,才会调用。    NSLog(@"signalOfsignals:%@",x);}];// 信号的信号发送信号[signalOfsignals sendNext:signal];// 信号发送内容[signal sendNext:@"hi"];

------- 组合 -------

组合就是将多个信号按照某种规则进行拼接,合成新的信号。

4. concat

按顺序拼接信号,当多个信号发出的时候,有顺序的接收信号。

//拼接信号 signalA、 signalB、 signalCRACSignal signalA = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {    [subscriber sendNext:@"Hello"];    [subscriber sendCompleted];    return nil;}];RACSignal signalB = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {    [subscriber sendNext:@"World"];    [subscriber sendCompleted];    return nil;}];RACSignal signalC = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {    [subscriber sendNext:@"!"];    [subscriber sendCompleted];    return nil;}];// 拼接 A B, 把signalA拼接到signalB后,signalA发送完成,signalB才会被激活。RACSignal concatSignalAB = [signalA concat:signalB];// A B + CRACSignal concatSignalABC = [concatSignalAB concat:signalC];// 订阅拼接的信号, 内部会按顺序订阅 A->B->C// 注意:第一个信号必须发送完成,第二个信号才会被激活...[concatSignalABC subscribeNext:^(id x) {    NSLog(@"%@", x);}];

5. then

用于连接两个信号,当第一个信号完成,才会连接then返回的信号。
then方法会等待completed事件的发送,然后再订阅由then block返回的signal。这样就高效地把控制权从一个signal传递给下一个。
注意使用then,之前信号的值会被忽略掉

6. merge

合并信号,任何一个信号发送数据,都能监听到。
合并信号被订阅的时候,就会遍历所有信号,并且发出这些信号。
合并信号一被订阅,就会订阅里面所有的信号。
只要有一个信号被发出就会被监听。

RACSignal signalA = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {      [subscriber sendNext:@"A"];      return nil;  }];  RACSignal signalB = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {      [subscriber sendNext:@"B"];      return nil;  }];  // 合并信号, 任何一个信号发送数据,都能监听到  RACSignal mergeSianl = [signalA merge:signalB];  [mergeSianl subscribeNext:^(id x) {      NSLog(@"%@", x);  }];//输出//A//B

7. combineLatest

将多个信号合并起来,并且拿到各个信号最后一个值,必须每个合并的signal至少都有过一次sendNext,才会触发合并的信号。
必须两个信号都发出内容,才会被触发, 并且把两个信号的 最后一次 发送的值组合成元组发出。

RACSignal signalA = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        [subscriber sendNext:@"A1"];        [subscriber sendNext:@"A2"];        return nil;    }];        RACSignal signalB = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        [subscriber sendNext:@"B1"];        [subscriber sendNext:@"B2"];        [subscriber sendNext:@"B3"];        return nil;    }];        RACSignal combineSianal = [signalA combineLatestWith:signalB];        [combineSianal subscribeNext:^(id x) {        NSLog(@"combineLatest:%@", x);    }];/输出2018-02-01 17:39:04.914804+0800 RAC 101[21958:29964626] combineLatest:<RACTwoTuple: 0x60400000fc70> (    A2,    B1)2018-02-01 17:39:04.915092+0800 RAC 101[21958:29964626] combineLatest:<RACTwoTuple: 0x60000000faa0> (    A2,    B2)2018-02-01 17:39:04.915270+0800 RAC 101[21958:29964626] combineLatest:<RACTwoTuple: 0x60000000fff0> (    A2,    B3)///如果用[signalB combineLatestWith:signalA],那么结果就是(B3, A1)  (B3, A2)

8. reduce

把信号发出元组的值聚合成一个值
reduce 合并信号的数据,进行汇总计算使用
订阅聚合信号,每次有内容发出,就会执行reduceblcok,把信号内容转换成reduceblcok返回的值。
常见的用法,(先组合在聚合)一般跟combineLatest 一起使用

//判断用户名和密码同时存在的时候,按钮才能点击[[RACSignal combineLatest:@[self.nameField.rac_textSignal, self.passwordField.rac_textSignal] reduce:^id _Nullable(NSString name, NSString pwd){        return @(name.length > 0 && pwd.length > 0);    }] subscribeNext:^(id  _Nullable x) {        _btn.enabled = [x boolValue];    }];

9. zip

把两个信号压缩成一个信号,只有当两个信号 同时 发出信号内容时,并且把两个信号的内容合并成一个元组,才会触发压缩流的next事件。
1. 定义压缩信号,内部就会自动订阅signalA,signalB
2. 每当signalA或者signalB发出信号,就会判断signalA,signalB有没有发出个信号,有就会把每个信号 第一次 发出的值包装成元组发出
注意:combineLatest与zip用法相似,必须每个合并的signal至少都有过一次sendNext,才会触发合并的信号。

RACSignal signalA = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        [subscriber sendNext:@"A1"];        [subscriber sendNext:@"A2"];        return nil;    }];        RACSignal signalB = [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        [subscriber sendNext:@"B1"];        [subscriber sendNext:@"B2"];        [subscriber sendNext:@"B3"];        return nil;    }];        RACSignal zipSignal = [signalA zipWith:signalB];        [zipSignal subscribeNext:^(id x) {        NSLog(@"%@", x);    }];/结果:2018-02-01 18:00:04.585460+0800 RAC 101[22558:30088028] <RACTwoTuple: 0x604000005840> (    A1,    B1)2018-02-01 18:00:04.585790+0800 RAC 101[22558:30088028] <RACTwoTuple: 0x6040000058d0> (    A2,    B2)/

10. filter

过滤信号,使用它可以获取满足条件的信号

// 每次信号发出,会先执行过滤条件判断.[[_textField.rac_textSignal filter:^BOOL(NSString value) {    NSLog(@"原信号: %@", value);    // 过滤 长度 <= 3 的信号    return value.length > 3;}] subscribeNext:^(id x) {    NSLog(@"长度大于3的信号:%@", x);}];

11. ignore

忽略某些信号
底层调用了 filter 与 过滤值进行比较,若相等返回则 NO

//过滤掉值为111的信号[[_textField.rac_textSignal ignore:@"111"] subscribeNext:^(id x) {        NSLog(@"%@",x);    }];

12. distinctUntilChanged

当上一次的值和当前的值有明显的变化就会发出信号,否则会被忽略掉。

[[_textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {      NSLog(@"%@",x);  }];

13. skip

跳过第N次的发送的信号。

// 表示输入第一次,不会被监听到,跳过第一次发出的信号[[_textField.rac_textSignal skip:1] subscribeNext:^(id x) { NSLog(@"%@",x);}];

14. take

取前N次的发送的信号。

RACSubject subject = [RACSubject subject] ;  // 取 前两次 发送的信号  [[subject take:2] subscribeNext:^(id x) {      NSLog(@"%@", x);  }];  [subject sendNext:@1];  [subject sendNext:@2];  [subject sendNext:@3];  // 输出  2017-01-03 17:35:54.566 ReactiveCocoa进阶[4969:1677908] 1  2017-01-03 17:35:54.567 ReactiveCocoa进阶[4969:1677908] 2

15. takeLast

取最后N次的发送的信号
前提条件,订阅者必须调用完成 sendCompleted,因为只有完成,就知道总共有多少信号.

RACSubject subject = [RACSubject subject] ;  // 取 后两次 发送的信号  [[subject takeLast:2] subscribeNext:^(id x) {      NSLog(@"%@", x);  }];    [subject sendNext:@1];  [subject sendNext:@2];  [subject sendNext:@3];  // 必须 跳用完成  [subject sendCompleted];

16. takeUntil

获取信号直到某个信号执行完成

// 监听文本框的改变直到当前对象被销毁[_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];

17. switchToLatest

用于signalOfSignals(信号的信号),有时候信号也会发出信号,会在signalOfSignals中,获取signalOfSignals发送的最新信号。
switchToLatest:只能用于信号中的信号

RACSubject signalOfSignals = [RACSubject subject];    RACSubject signal = [RACSubject subject];        // 获取信号中信号最近发出信号,订阅最近发出的信号。    [signalOfSignals.switchToLatest subscribeNext:^(id x) {        NSLog(@"%@", x);    }];        [signalOfSignals sendNext:signal];    [signal sendNext:@1];

---- 秩序 ----

秩序包括 doNext 和 doCompleted 这两个方法,主要是在 执行sendNext 或者 sendCompleted 之前,先执行这些方法中Block。

18. doNext

执行sendNext之前,会先执行这个doNext的 Block

19. doCompleted

执行sendCompleted之前,会先执行这doCompletedBlock

[[[[RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        [subscriber sendNext:@"hi"];        [subscriber sendCompleted];        return nil;            }] doNext:^(id x) {        // 执行 [subscriber sendNext:@"hi"] 之前会调用这个 Block        NSLog(@"doNext");            }] doCompleted:^{        // 执行 [subscriber sendCompleted] 之前会调用这 Block        NSLog(@"doCompleted");            }] subscribeNext:^(id x) {        NSLog(@"%@", x);    }];/运行结果:2018-02-02 09:56:39.206710+0800 RAC 101[40487:30848792] doNext2018-02-02 09:56:39.206954+0800 RAC 101[40487:30848792] hi2018-02-02 09:56:39.207112+0800 RAC 101[40487:30848792] doCompleted/

---- 线程 ----

ReactiveCocoa 中的线程操作 包括 deliverOnsubscribeOn这两种,将 传递的内容 或 创建信号时 block中的代码 切换到指定的线程中执行。

20. deliverOn

内容传递切换到指定线程中,副作用在原来线程中,把在创建信号时block中的代码称之为副作用。

// 在子线程中执行    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [[[RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {            NSLog(@"%@", [NSThread currentThread]);            [subscriber sendNext:@123];            [subscriber sendCompleted];            return nil;        }]         deliverOn:[RACScheduler mainThreadScheduler]]         subscribeNext:^(id x) {             NSLog(@"%@", x);             NSLog(@"%@", [NSThread currentThread]);         }];    });/运行结果:2018-02-02 14:10:28.938587+0800 RAC 101[45182:31032478] <NSThread: 0x604000279c00>{number = 3, name = (null)}2018-02-02 14:10:29.076247+0800 RAC 101[45182:31031131] 1232018-02-02 14:10:29.076494+0800 RAC 101[45182:31031131] <NSThread: 0x60400006f540>{number = 1, name = main}/可以看到 副作用 在 子线程 中执行,而 传递的内容 在 主线程 中接收

21. subscribeOn

subscribeOn则是将 内容传递 和 副作用 都会切换到指定线程中。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [[[RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {            NSLog(@"%@", [NSThread currentThread]);            [subscriber sendNext:@123];            [subscriber sendCompleted];            return nil;        }]         subscribeOn:[RACScheduler mainThreadScheduler]] //传递的内容到主线程中         subscribeNext:^(id x) {             NSLog(@"%@", x);             NSLog(@"%@", [NSThread currentThread]);         }];    });/运行结果:2018-02-02 14:15:23.956524+0800 RAC 101[45319:31066198] <NSThread: 0x600000079940>{number = 1, name = main}2018-02-02 14:15:23.956737+0800 RAC 101[45319:31066198] 1232018-02-02 14:15:23.956961+0800 RAC 101[45319:31066198] <NSThread: 0x600000079940>{number = 1, name = main}/可以看到,内容传递 和 副作用 都切换到了 主线程 执行

----时间----

时间操作就会设置信号超时,定时和延时。

22. interval

定时:每隔一段时间发出信号

// 每隔1秒发送信号,指定当前线程执行    [[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {        NSLog(@"定时:%@", x);    }];

23. timeout

超时,可以让一个信号在一定的时间后,自动报错。

RACSignal signal = [[RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        // 不发送信号,模拟超时状态        // [subscriber sendNext:@"hello"];        //[subscriber sendCompleted];        return nil;    }] timeout:1 onScheduler:[RACScheduler currentScheduler]];// 设置1秒超时        [signal subscribeNext:^(id x) {        NSLog(@"%@", x);    } error:^(NSError error) {        NSLog(@"%@", error);    }];        // 执行代码 1秒后 输出:    2017-01-04 13:48:55.195 ReactiveCocoa进阶[1980:492724] Error Domain=RACSignalErrorDomain Code=1 "(null)"

24. retry

重试:只要 发送错误 sendError: 就会重新执行创建信号的Block直到成功

[[[RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        if (i == 3) {            [subscriber sendNext:@"Hello"];        } else {            // 发送错误            NSLog(@"收到错误:%d", i);            [subscriber sendError:nil];        }        i++;        return nil;    }] retry] subscribeNext:^(id x) {        NSLog(@"%@", x);    } error:^(NSError error) {        NSLog(@"%@", error);    }];// 输出2017-01-04 14:36:51.594 ReactiveCocoa进阶[2443:667226] 收到错误信息:02017-01-04 14:36:51.595 ReactiveCocoa进阶[2443:667226] 收到错误信息:12017-01-04 14:36:51.595 ReactiveCocoa进阶[2443:667226] 收到错误信息:22017-01-04 14:36:51.596 ReactiveCocoa进阶[2443:667226] Hello

25. replay

重放:当一个信号被多次订阅,反复播放内容

RACSignal signal = [[RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) {        [subscriber sendNext:@1];        [subscriber sendNext:@2];        return nil;    }] replay];        [signal subscribeNext:^(id x) {        NSLog(@"%@", x);    }];        [signal subscribeNext:^(id x) {        NSLog(@"%@", x);    }];/运行结果:2018-02-02 14:37:45.269758+0800 RAC 101[45938:31201667] 12018-02-02 14:37:45.270018+0800 RAC 101[45938:31201667] 22018-02-02 14:37:45.270175+0800 RAC 101[45938:31201667] 12018-02-02 14:37:45.270300+0800 RAC 101[45938:31201667] 2/

26. throttle

节流:当某个信号发送比较频繁时,可以使用节流,在某一段时间不发送信号内容,过了一段时间获取信号的最新内容发出。

RACSubject subject = [RACSubject subject];    // 节流1秒,1秒后接收最后一个发送的信号    [[subject throttle:1] subscribeNext:^(id x) {        NSLog(@"%@", x);    }];    [subject sendNext:@1];    [subject sendNext:@2];    [subject sendNext:@3];        // 输出    2018-02-02 14:53:00.234975+0800 RAC 101[46278:31282576] 3

扩展:利用RAC封装网络请求

通过RAC,我们可以奖网络请求封装为信号的形式,数据的获取可以通过订阅信号的方式来得到。与传统的网络请求相比,是不是感觉耳目一新?

#import <AFNetworking/AFNetworking.h>#import "AFNetworking.h"#import <ReactiveObjC.h>@interface NetworkingManager : AFHTTPSessionManager+ (instancetype)shareManager;- (RACSignal )GET:(NSString )url parameters:(id)parameters;@end--------------------------------------------------------------------@implementation NetworkingManager+ (instancetype)shareManager{    static NetworkingManager manager;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        manager = [[self alloc]init];        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html" ,nil];    });    return manager;}//GET请求- (RACSignal )GET:(NSString )url parameters:(id)parameters{    return [RACSignal createSignal:^RACDisposable  _Nullable(id<RACSubscriber>  _Nonnull subscriber) {        [self GET:url parameters:parameters progress:nil success:^(NSURLSessionDataTask  _Nonnull task, id  _Nullable responseObject) {            [subscriber sendNext:responseObject];            [subscriber sendCompleted];        } failure:^(NSURLSessionDataTask  _Nullable task, NSError  _Nonnull error) {            [subscriber sendError:error];        }];        return nil;    }];}@end--------------------------------------------------------------------//请求网络数据- (void)fetchWeatherData{    NetworkingManager manager = [NetworkingManager shareManager];    [[manager GET:@"http://www.weather.com.cn/data/sk/101010100.html" parameters:nil] subscribeNext:^(id  _Nullable x) {        NSLog(@"%@", x);    } error:^(NSError  _Nullable error) {        NSLog(@"%@", error.localizedDescription);    }];}//如上:调用请求,订阅信号后得到的x就是需要的json数据。
网友评论