RAC系列2之 基础篇
RAC简介
- ReactiveCocoa(简称为RAC),是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。
- ReactiveCocoa作用
- ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合的思想。
ReactiveCocoa编程思想
- ReactiveCocoa结合了几种编程风格:
- 函数式编程(Functional Programming)
- 响应式编程(Reactive Programming)
- ReactiveCocoa被描述为函数响应式编程(FRP)框架。
- 使用RAC解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。
导入ReactiveCocoa框架
- 之前GitHub上面的该框架是用ReactiveCocoa导入,但是现在该框架是swift版本的了,要想使用OC版本的就是ReactiveObjC了
- 导入:
-
vim podfile
->键盘输入i
,然后编辑:source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'RAC的学习'do use_frameworks! pod'ReactiveObjC' end
-
esc->:wq->pod install
-
RAC常见的类
- RAC中:万物皆信号,任何事件通过信号传递
- 信号源:信号源代表的是随着时间而改变的值流,订阅者可以通过订阅信号源来获取这些值;
- 你可以把它想象成水龙头中的水,当你打开水龙头时,水源源不断地流出来;你也可以把它想象成电,当你插上插头时,电静静地充到你的手机上;这里的水、电就是我们所需要的值,而打开水龙头、插上插头就是订阅它们的过程。
RACSiganl
- 信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。
- 信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。
- 默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。
- 如何订阅信号:调用信号RACSignal的subscribeNext就能订阅。
-
简单使用
//1.创建信号 RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { //didSubscribe:当前block // block调用时刻:每当有订阅者订阅信号,就会调用block。 // didSubscribe作用: 发送数据 // 3.发送信号 [subscriber sendNext:@1]; // 如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。 [subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{ //block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号 // 执行完Block后,当前信号就不在被订阅了。 NSLog(@"信号被销毁"); }]; }]; //2. 订阅信号,才会激活信号(相当于响应式编程中的监听) [signal subscribeNext:^(id _Nullable x) { //nextBlock:当前block // block调用时刻:每当有信号发出数据,就会调用block. //nextBlock作用: 处理数据展示到UI上面 //x:信号发送内容 NSLog(@"接收到数据:%@",x); } error:^(NSError * _Nullable error) { NSLog(@"订阅信号发生错误"); } completed:^{ NSLog(@"订阅完成"); }];
- RACSignal创建方法的(create…)底层实现:
- 创建信号
RACSignal
的子类RACDynamicSignal
对象,首先把didSubscribe
(这个block)保存到信号RACDynamicSignal
对象中(就是设置属性的意思),还不会触发。 - 当信号被订阅,也就是调用
signal的subscribeNext:nextBlock
内部做以下事情:- 创建订阅者
subscriber
,并且把nextBlock
保存到subscriber
中。 - 调用
siganl
的didSubscribe
block。
- 创建订阅者
siganl
的didSubscribe
中调用[subscriber sendNext:@1];
sendNext
底层其实就是执行subscriber
的nextBlock
- 创建信号
RACSubscriber
- 表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者
- 这个协议声明了4个方法:
- 发送信号:
- (void)sendNext:(nullable id)value
- 发送错误信息:
- (void)sendError:(nullable NSError *)error;
- 发送完成:
- (void)sendCompleted;
- 取消发送信号:
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
- 发送信号:
- 通过create创建的信号,都有一个订阅者,帮助他发送数据。
RACDisposable
- 用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
- 使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。
-
举例使用
#import "ViewController.h" #import <ReactiveObjC/ReactiveObjC.h> @interface ViewController () @property (nonatomic, strong) id<RACSubscriber> subscriber; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.创建信号 RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber ) { _subscriber = subscriber; // 3.发送信号 [subscriber sendNext:@"123"]; return [RACDisposable disposableWithBlock:^{ // 只要信号取消订阅就会来这 // 清空资源 NSLog(@"信号被取消订阅了"); }]; }]; // 2.订阅信号 RACDisposable *disposable = [signal subscribeNext:^(id x) { NSLog(@"%@",x); }]; // 默认一个信号发送数据完毕就会自动取消订阅. // 但是只要订阅者在,就不会自动取消信号订阅(用属性强引用着订阅者,就不会自动取消订阅了) // 那么我们就需要(主动取消)取消订阅信号 [disposable dispose]; }
RACSubject与RACReplaySubject
RACSubject
-
信号提供者,自己可以充当信号,又能发送信号。
@interface RACSubject<ValueType> : RACSignal<ValueType> <RACSubscriber> /// Returns a new subject. + (instancetype)subject; // Redeclaration of the RACSubscriber method. Made in order to specify a generic type. - (void)sendNext:(nullable ValueType)value; @end
- 使用场景:
- 通常用来代替代理,有了它,就不必要定义代理了。
- 基本使用步骤:
- 创建信号
[RACSubject subject]
,跟RACSiganl
不一样,创建信号时没有block
。 - 订阅信号
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
- 发送信号
sendNext:(id)value
- 创建信号
-
代码举例:
//1.创建信号(此时代表信号) RACSubject *subject = [RACSubject subject]; //2.订阅信号 //RACSubject处理订阅:仅仅是保存订阅者 [subject subscribeNext:^(id _Nullable x) { NSLog(@"订阅者第一次接收到数据:%@",x); }]; [subject subscribeNext:^(id _Nullable x) { NSLog(@"订阅者第二次接收到数据:%@",x); }]; //3. 发送数据(信号)(此时代表订阅者) //底层实现:遍历所有的订阅者,调用nextBlock [subject sendNext:@"1"];
打印:
2017-12-26 11:35:48.235200+0800 RAC的学习[3905:178019] 订阅者第一次接收到数据:1 2017-12-26 11:35:48.235323+0800 RAC的学习[3905:178019] 订阅者第二次接收到数据:1
- 底层实现:(底层实现和RACSignal不一样)
[RACSubject subject]:
- 内部会创建一个可变数组赋值给属性
- 调用
subscribeNext
订阅信号:- 创建订阅者,把
nextblock
赋值给订阅者属性 - 将该订阅者保存到属性数组中
- 创建订阅者,把
- 调用
sendNext
发送信号:- 遍历数组属性,拿到所有的订阅者,一个一个调用订阅者的
nextBlock
。
- 遍历数组属性,拿到所有的订阅者,一个一个调用订阅者的
- RACSubject替换代理
- 需求:
- 给当前控制器添加一个按钮,modal到另一个控制器界面
- 另一个控制器view中有个按钮,点击按钮,通知当前控制器
步骤一:在第二个控制器.h,添加一个RACSubject代替代理。 @interface TwoViewController : UIViewController @property (nonatomic, strong) RACSubject *delegateSignal; @end 步骤二:监听第二个控制器按钮点击 @implementation TwoViewController - (IBAction)notice:(id)sender { // 通知第一个控制器,告诉它,按钮被点了 // 通知代理 // 判断代理信号是否有值 if (self.delegateSignal) { // 有值,才需要通知 [self.delegateSignal sendNext:nil]; } } @end 步骤三:在第一个控制器中,监听跳转按钮,给第二个控制器的代理信号赋值,并且监听. @implementation OneViewController - (IBAction)btnClick:(id)sender { // 创建第二个控制器 TwoViewController *twoVc = [[TwoViewController alloc] init]; // 设置代理信号 twoVc.delegateSignal = [RACSubject subject]; // 订阅代理信号 [twoVc.delegateSignal subscribeNext:^(id x) { NSLog(@"点击了通知按钮"); }]; // 跳转到第二个控制器 [self presentViewController:twoVc animated:YES completion:nil]; } @end
- 需求:
RACReplaySubject
- 重复提供信号类,
RACSubject
的子类。 RACReplaySubject
使用场景- 如果一个信号每被订阅一次,就需要把之前的值重复发送一遍,使用重复提供信号类
- 可以设置
capacity
数量来限制缓存的value的数量,即只缓充最新的几个值。
RACReplaySubject
与RACSubject
区别:- RACReplaySubject可以先发送信号,再订阅信号,RACSubject就不可以。
- 基本使用步骤
- 创建信号
[RACReplaySubject subject]
,跟RACSiganl
不一样,创建信号时没有block
。 - 可以先订阅信号,也可以先发送信号。
- 订阅信号
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
- 发送信号
sendNext:(id)value
- 订阅信号
- 创建信号
-
代码举例
//1.创建信号 RACReplaySubject *replaySubject = [RACReplaySubject subject]; //2.发送信号 //1.保存值 2. 遍历所有的订阅者,发送数据 [replaySubject sendNext:@1]; [replaySubject sendNext:@2]; //3. 订阅信号 [replaySubject subscribeNext:^(id _Nullable x) { NSLog(@"第一个订阅者拿到的数据===%@",x); }]; [replaySubject subscribeNext:^(id _Nullable x) { NSLog(@"第二个订阅者拿到的数据===%@",x); }];
打印:
2017-12-26 11:22:46.368676+0800 RAC的学习[3739:164003] 第一个订阅者拿到的数据===1 2017-12-26 11:22:46.368902+0800 RAC的学习[3739:164003] 第一个订阅者拿到的数据===2 2017-12-26 11:22:46.369426+0800 RAC的学习[3739:164003] 第二个订阅者拿到的数据===1 2017-12-26 11:22:46.369611+0800 RAC的学习[3739:164003] 第二个订阅者拿到的数据===2
- 底层实现:(底层实现和RACSubject不一样)
- 创建
[RACReplaySubject subject]
:- 尽管
subject
方法是父类的,但是调用init
会有优先调用子类RACReplaySubject
的init
- 初始化一个可变数组属性用于存储值
- 尽管
- 调用
sendNext
发送信号:- 把值保存到数组属性中
- 调用父类
RACSubject sendNext:
遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock
。
- 调用subscribeNext订阅信号:
- 遍历保存的所有值,一个一个调用订阅者的
nextBlock
(一个订阅者遍历值调用)
- 遍历保存的所有值,一个一个调用订阅者的
- 创建
- 订阅信号与发送信号顺序不同的区别
- 如果先订阅再发送:
- 跟RACSubject效果一样
- 如果先发送再订阅:
- 也就是先保存值,在订阅值。
- 一旦订阅就回迅速拿到之前发送的所有值
- 如果想当一个信号被订阅,就重复播放之前所有值,需要先发送信号,在订阅信号。
- 如果先订阅再发送:
RAC中的集合类
RACTuple
- 元组类,类似NSArray,用来包装值.
-
使用方法跟NSArray基本一样
//1. 创建 RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"123",@"234",@"345"]]; // [RACTuple tupleWithObjects:@"123",@"444", nil] //2. 取值 NSString *str = tuple[0]; // [tuple objectAtIndex:0]; //3. 添加(这里是返回一个新值(RACTuple),并不是可变数据的特性) RACTuple *tuple2 = [tuple tupleByAddingObject:@"555"]; NSLog(@"%@",str); //4.RAC特有用法:遍历 [tuple.rac_sequence.signal subscribeNext:^(id _Nullable x) { NSLog(@"======%@",x); }]; //5.其他用法 //5.1 获取数量 NSLog(@"%zd",tuple.count); //5.2 获取首尾 NSLog(@"%@===%@",tuple.first,tuple.last); //5.3 RACTuple转换成数组类型 NSArray *array = [tuple allObjects]; NSLog(@"%@",array); NSLog(@"tuple2===%@",[tuple2 allObjects]);
RACSequence
- RAC中的集合媒介类
- 作用:起到中间的媒介作用,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
- 把集合(
NSArray,NSDictionary,RACTuple
等)转换成RACSequence类:集合.rac_sequence
- 把RACSequence类转换RACSignal信号类:
集合.rac_sequence.signal
-
订阅信号,激活信号,会自动把集合中的所有值,遍历出来。:
[集合.rac_sequence.signal subscribeNext:^(id x) { NSLog(@"%@",x); }];
- 举例使用:
-
遍历数组
NSArray *array = @[@"124",@"22",@"44"]; //1. 把数组转换成集合RACSequence array.rac_sequence RACSequence *sequence = array.rac_sequence; //2. 把集合RACSequence转换RACSignal信号类,array.rac_sequence.signal RACSignal *signal = sequence.signal; //3. 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。 [signal subscribeNext:^(id _Nullable x) { NSLog(@"%@",x); }]; //以上3步整合成一步 [array.rac_sequence.signal subscribeNext:^(id _Nullable x) { NSLog(@"%@",x); }];
-
遍历字典
//遍历出来的键值对会包装成RACTuple(元组对象) NSDictionary *dic = @{ @"name" : @"xiaoli", @"year" : @20, @"sex" : @"男" }; //遍历 [dic.rac_sequence.signal subscribeNext:^(RACTuple * x) { //打印x类型为RACTwoTuple(只放2个值,RACTuple的子类),因此在这里改变id类型为RACTuple类型 NSLog(@"%@",x); //解析字典 //方法1: NSString *key = x[0]; NSString *value = x[1]; NSLog(@"key==%@=value==%@",key,value); //方法2: //RACTupleUnpack:宏,解包元组,会把元组的值,按顺序给参数里面的变量赋值."="右边放需解析的元组. RACTupleUnpack(NSString *key2,NSString *value2) = x; NSLog(@"key2==%@=value2==%@",key2,value2); }];
-
字典转模型
// 3.1 OC写法 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; NSMutableArray *items = [NSMutableArray array]; _items = items; for (NSDictionary *dict in dictArr) { FlagItem *item = [FlagItem flagWithDict:dict]; [items addObject:item]; } // 3.2 RAC写法 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; NSMutableArray *flags = [NSMutableArray array]; _flags = flags; // rac_sequence注意点:调用subscribeNext,并不会马上执行nextBlock,而是会等一会。 [dictArr.rac_sequence.signal subscribeNext:^(id x) { // 运用RAC遍历字典,x:字典 FlagItem *item = [FlagItem flagWithDict:x]; [flags addObject:item]; }]; NSLog(@"%@", NSStringFromCGRect([UIScreen mainScreen].bounds)); // 3.3 RAC高级写法: NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]; NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; // map:映射的意思,目的:把原始值value映射成一个新值 // array: 把集合转换成数组 // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。 NSArray *flags = [[dictArr.rac_sequence map:^id(id value) { return [FlagItem flagWithDict:value]; }] array];
-
- 把集合(
RACMulticastConnection
- 用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。
- 注意:
- 只针对RACSignal的创建方式,不针对RACSubject,因为RACSubject创建方式中不含block!!!
RACMulticastConnection
通过RACSignal的-publish
或者-muticast:
方法创建.
- RACMulticastConnection使用步骤:
- 创建信号
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
- 创建连接
RACMulticastConnection *connect = [signal publish];
- 订阅信号,注意:订阅的不在是之前的信号,而是连接的信号。
[connect.signal subscribeNext:nextBlock]
- 连接
[connect connect]
- 创建信号
- 代码举例
-
引出:
//1.创建请求信号 RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { NSLog(@"创建信号中的block:AFN发送网络请求"); //3.网络请求完毕,返回数据,发送信号,将数据传出 [subscriber sendNext:@1]; return nil; }]; //2. 订阅信号 [signal subscribeNext:^(id _Nullable x) { NSLog(@"订阅者1===%@",x); }]; [signal subscribeNext:^(id _Nullable x) { NSLog(@"订阅者2===%@",x); }];
-
打印结果如下:
2018-01-02 10:57:04.804152+0800 RAC的学习[6544:226938] 创建信号中的block:AFN发送网络请求 2018-01-02 10:57:04.804411+0800 RAC的学习[6544:226938] 订阅者1===1 2018-01-02 10:57:04.804694+0800 RAC的学习[6544:226938] 创建信号中的block:AFN发送网络请求 2018-01-02 10:57:04.804855+0800 RAC的学习[6544:226938] 订阅者2===1
- 从打印可以看出:
- 创建信号中的block被调用了2次
- 引起弊端:在一个信号中发送请求,每次订阅一次都会发送请求,这样就会导致多次请求。
- 要求:
- 每次订阅不要都请求一次,只请求一次,每次订阅都要拿到第一次请求的数据
//1.创建请求信号 RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { NSLog(@"创建信号中的block:AFN发送网络请求"); //4.网络请求完毕,返回数据,发送信号,将数据传出 [subscriber sendNext:@1]; return nil; }]; //2. 创建连接(把信号转换成连接类) // RACMulticastConnection *connection = [signal publish]; RACMulticastConnection *connection = [signal multicast:[RACReplaySubject subject]]; //3. 订阅连接类信号 // 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext: [connection.signal subscribeNext:^(id _Nullable x) { NSLog(@"订阅者1===%@",x); }]; [connection.signal subscribeNext:^(id _Nullable x) { NSLog(@"订阅者2===%@",x); }]; //5.连接,激活信号 [connection connect];
-
打印结果如下:
2018-01-02 11:07:38.938790+0800 RAC的学习[6701:245354] 创建信号中的block:AFN发送网络请求 2018-01-02 11:07:38.939491+0800 RAC的学习[6701:245354] 订阅者1===1 2018-01-02 11:07:38.939804+0800 RAC的学习[6701:245354] 订阅者2===1
-
- 底层原理
- 创建
connect
:(publish/multicast
)publish
创建其实与multicast
创建一样,因为publish
内部会通过multicast
创建multicast
内部做如下事情:- 新建
RACSubject
对象subect
- 初始化
RACMulticastConnection
对象connect
- 将
signal
赋值给connect
对象的属性sourceSignal
;subject
赋值给connect
对象的属性signal
- 新建
connection.signal
订阅信号- 本质是拿到
subject
(从上面的可以看出)订阅 - 订阅信号,此时用的是
subject
订阅信号,跟sourceSignal
无关,因此此时的订阅不会调用源信号的block
subject
订阅信号原理可知,会保存所有的订阅者
- 本质是拿到
[connection connect]
- 拿到源信号
sourceSignal
,订阅新信号signal(subject)
(此时subject
就是充当订阅者了,不是信号了)[self.sourceSignal subscribe:_signal]
- 因为这里只调用了源信号一次,因此,源信号的
block
只会调用一次,但是源信号block
中传出的订阅者可是subject
- 源信号中的订阅者
subject
,发送信号:didSubscribe
,拿到订阅者调用sendNext
,其实是调用RACSubject
的sendNext
- 根据
subject
发送信号的原理,会遍历subject
保存的所有订阅者发送信号
- 拿到源信号
- 创建
RACCommand
- RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。
- 使用场景:监听按钮点击,网络请求
- RACCommand使用步骤:
- 创建命令
initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
- 在
signalBlock
中,创建RACSignal
,并且作为signalBlock
的返回值 - 执行命令
- (RACSignal *)execute:(id)input
- 创建命令
- RACCommand使用注意:
signalBlock
必须要返回一个信号,不能传nil
.- 如果不想要传递信号,直接创建空的信号
[RACSignal empty];
RACCommand
中信号如果数据传递完,必须调用[subscriber sendCompleted]
,这时命令才会执行完毕,否则永远处于执行中。
- RACCommand设计思想:内部
signalBlock
为什么要返回一个信号,这个信号有什么用。- 在RAC开发中,通常会把网络请求封装到
RACCommand
,直接执行某个RACCommand
就能发送请求 - 当
RACCommand
内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock
返回的信号传递了。
- 在RAC开发中,通常会把网络请求封装到
- 如何拿到RACCommand中返回信号发出的数据。
RACCommand
有个执行信号源executionSignals
,这个是signal of signals
(信号的信号),意思是信号发出的数据是信号,不是普通的类型。- 订阅
executionSignals
就能拿到RACCommand
中返回的信号,然后订阅signalBlock
返回的信号,就能获取发出的值。
- 监听当前命令是否正在执行
executing
-
代码举例:
如何拿到执行命令中产生的数据: 订阅命令内部的信号//1. 创建命令 RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) { //input:执行命令传入的参数 //signalBlock调用时刻:执行命令的时候调用 NSLog(@"%@",input); // 2.创建信号,用来传递数据 //创建空信号 // return [RACSignal empty]; return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { //发送网络请求 NSLog(@"AFN网络请求"); //网络请求完毕,将数据发出 [subscriber sendNext:@"网络请求的到的数据"]; // 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。 [subscriber sendCompleted]; return nil; }]; }];
方法1:直接订阅执行命令返回的信号(先执行命令,在订阅信号)
//3.执行命令 RACSignal *signal = [command execute:@1]; //4. 订阅信号 [signal subscribeNext:^(id _Nullable x) { NSLog(@"===%@",x); }];
方法2:先订阅信号,在执行命令
//3. 订阅RACCommand中的信号 //3.1 基本用法: //executionSignals:信号源,信号中信号signal of signals信号,发送数据就是信号 [command.executionSignals subscribeNext:^(RACSignal * x) { [x subscribeNext:^(id x) { NSLog(@"%@",x); }]; } //3.2 RAC高级用法 //switchToLatest:用于signal of signals,获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号 // [command.exutionSignals.switchToLatest subscribeNext:^(id x) { // // NSLog(@"%@",x); // }];
4.监听信号(前提是订阅者发送完毕后一定要调用
[subscriber sendCompleted];
)
不使用skip[command.executing subscribeNext:^(NSNumber * _Nullable x) { //x为bool型变量 if ([x boolValue] == YES) { // 正在执行 NSLog(@"正在执行"); }else{ // 执行完成 NSLog(@"执行完成"); } }];
打印结果:
2018-01-02 15:20:22.091727+0800 RAC的学习[9676:454456] 执行完成 2018-01-02 15:20:22.092156+0800 RAC的学习[9676:454456] 1 2018-01-02 15:20:22.093439+0800 RAC的学习[9676:454456] 正在执行 2018-01-02 15:20:22.093679+0800 RAC的学习[9676:454456] AFN网络请求 2018-01-02 15:20:22.093902+0800 RAC的学习[9676:454456] 网络请求的到的数据 2018-01-02 15:20:22.094442+0800 RAC的学习[9676:454456] 执行完成
监听命令是否执行完毕,默认会来一次,可以直接跳过,skip表示跳过第一次信号。
[[command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) { //x为bool型变量 if ([x boolValue] == YES) { // 正在执行 NSLog(@"正在执行"); }else{ // 执行完成 NSLog(@"执行完成"); } }];
打印结果:
2018-01-0 2 15:22:58.319233+0800 RAC的学习[9784:459537] 1 2018-01-02 15:22:58.320647+0800 RAC的学习[9784:459537] 正在执行 2018-01-02 15:22:58.320914+0800 RAC的学习[9784:459537] AFN网络请求 2018-01-02 15:22:58.321147+0800 RAC的学习[9784:459537] 网络请求的到的数据 2018-01-02 15:22:58.321548+0800 RAC的学习[9784:459537] 执行完成
执行命令
//5. 执行命令 [command execute:@1];
-
创建信号中信号
RACSubject *signalofsignals = [RACSubject subject]; RACSubject *signalA = [RACSubject subject]; RACSubject *signalB = [RACSubject subject]; //订阅信号 [signalofsignals.switchToLatest subscribeNext:^(id _Nullable x) { NSLog(@"%@",x); }]; //发送信号 //信号发送信号 [signalofsignals sendNext:signalA]; [signalA sendNext:@1]; [signalB sendNext:@"B"];
- 监听按钮点击使用举例: 自动给按钮绑定了command,点击按钮,相当于执行
[command execute:btn];
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *loginButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//使用方式1:监听按钮点击
_loginButton.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
// comandBlock
NSLog(@"点击了按钮%@",input);
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 信号Block
[subscriber sendNext:input];
return nil;
}];
}];
// 监听点击结果
[_loginButton.rac_command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
//使用方式2:监听按钮点击,同时控制按钮的使能(enable)
//initWithEnabled
RACSubject *enableSignal = [RACSubject subject];
_loginButton.rac_command = [[RACCommand alloc] initWithEnabled:enableSignal signalBlock:^RACSignal * _Nonnull(id _Nullable input) {
// comandBlock
NSLog(@"点击了按钮%@",input);
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 信号Block
[subscriber sendNext:input];
// 请求完成的时候去调用(不调用这一句,command永远没完成,按钮只能点击一次)
[subscriber sendCompleted];
return nil;
}];
}];
//控制按钮的使能(加上这一句话,按钮的enable= NO)
//[enableSignal sendNext:NO];
//监听点击事件是否完成,来控制按钮的enable
[[_loginButton.rac_command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
BOOL executing = [x boolValue];
//控制按钮是否能够点击
//[enableSignal sendNext:NO];按钮的Enable= NO
[enableSignal sendNext:@(!executing)];
}];
// 监听点击结果
[_loginButton.rac_command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
}
@end
RACScheduler
- RAC中的队列,用GCD封装的。
RACUnit
- 表⽰stream不包含有意义的值,也就是看到这个,可以直接理解为nil.
RACEvent
- 把数据包装成信号事件(signal event)。它主要通过RACSignal的-materialize来使用,然并卵。