本编文章是我对 MVVM + RAC 知识的一些学习总结了分享,如错误的地方,希望可以各位大佬积极指正。⛽️
文章中引用了别人文章中很多的内容,在这里说明一下侵权删。
在学习RAC之前我们得了解一下什么是响应式编程(FRP: functional reactive programming 翻墙),因为RAC就是通过 函数式 + 响应式编程相结合实现的。
🌰 在我们编写代码的时候如:a = b + c 当我们更改 b 或 c 的时候,a 的值并不会立即改变,最起码你得重写运行一次代码才行,但是在响应式编程的世界里面,我们 改变 b 或者 c 的时候,a 的值也会发生改变,这就意味着它们之间出现了一只绑定的关系。是不是很神奇。。。。。。
一、配置RAC
在实际使用中,我们并不是直接查询ReactiveCocoa,它其实是一个总称,在ReactiveCocoa中分为Swift版的 ReactiveSwift和OC版本的
ReactiveObjC,这里我介绍的是ReactiveObjC部分。
使用cocoaPods在podfile中添加
pod 'ReactiveObjC', '~> 3.1.0' //或者 pod 'ReactiveObjC'
然后pod install一下。在项目中#import <ReactiveObjC.h>,建议放入pch头文件中。
二、RAC的一些简单介绍
RAC的一个主要优点是,它提供了处理异步行为的单一、统一的方法,包括委托方法、回调块、目标操作机制、通知和kvo
1. 基本类的使用
- RACSignal类的使用
// RACSignal的使用
-(void)RACSignal {
// 1.创建signal信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"%@",subscriber);
// subscriber并不是一个对象
// 3. 发送信号
[subscriber sendNext:@"123"];
// 4. 完成订阅
[subscriber sendCompleted];
// 4. 销毁信号
return [RACDisposable disposableWithBlock:^{
NSLog(@"销毁了");
}] ;
}];
// 2.1订阅
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
// 2.2 针对实际中可能出现的逻辑错误,RAC提供了订阅error信号
[signal subscribeError:^(NSError * _Nullable error) {
NSLog(@"%@",error);
}];
}
RACSignal 是一个信号类,只要有数据改变就会把数据包装成信号传递出去,但是注意发出信号的并不是 RACSignal 它不可以发送信号,发送信号的是 订阅者 subscriber ,并把nextBlock保存到订阅者里面,创建的时候会返回 [RACDynamicSignal createSignal:didSubscribe];调用RACDynamicSignal的didSubscribe,发送信号[subscriber sendNext:value],拿到订阅者的nextBlock调用。
- RACSubjec类的使用
// RACSubject(可自己发送信号也可以自己充当信号)
- (void)initRACSubject {
/*
1.创建RACSubject信号
2.订阅信号(subscribeNext)。本质就是创建订阅者,把订阅者放到数组里面。
3.发送信号(sendNext)。本质就是让订阅者数组里面的每一个订阅者都去调用 sendNext 方法。
*/
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"RACSubjectTest x = %@",x);
} error:^(NSError * _Nullable error) {
NSLog(@"%@",error);
} completed:^{
NSLog(@"11111");
}];
//发送信号
[subject sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"];
}
RACSubject最大的优点就是自己可以发送和接收信息,同时它是RACSignal的子类。在公司的项目我发现很多的时候都会用RACSubject来代替RACSignal作为返回值。
- RACReplaySubject类的使用
// RACReplaySubject
-(void)initRACReplaySubject{
// 1.1创建方法一
RACReplaySubject *repSubject = [RACReplaySubject subject];
// 1.2创建方法二: 通过设置capacity来限定它接收事件的数量
RACReplaySubject *repSubject2 = [RACReplaySubject replaySubjectWithCapacity:2];
// 2发送
[repSubject sendNext:@"repSubject"];
// 这里限制了repSubject2的最大接受事件为2 所以第一个会被移除
[repSubject2 sendNext:@"repSubject2"];
[repSubject2 sendNext:@"repSubject22"];
[repSubject2 sendNext:@"repSubject222"];
// 3订阅
[repSubject subscribeNext:^(id _Nullable x) {
NSLog(@"repSubject = %@",x);
}];
[repSubject2 subscribeNext:^(id _Nullable x) {
NSLog(@"repSubject2 = %@",x);
}];
}
RACReplaySubject作为RACSubject的子类自然会拥有父类的所有特性,同时它也有自己的特性,比如我们通过 replaySubjectWithCapacity 去创建RACReplaySubject那么就会限制它接收时间的数量。在它的内部,通过遍历来实现限制功能。
- RACTuple(元组)的使用
RACTuple内部就是封装了数组,用起来跟数组差不多,用过swift的小伙伴估计都知道元组的概念,在这就不多说了。
// RACTuple(元组)-- 其内部就是封装了数组,用起来跟数组差不多
-(void)initRACTuple{
//1.创建
// 1.1 通过定值创建RACTuple
RACTuple *tuple = [RACTuple tupleWithObjects:@"1",@"2",@"3", nil];
// 1.2 利用 RAC 宏快速封装
RACTuple *tuple2 = RACTuplePack(@"1", @"2", @"3");
// 1.3 从别的数组中获取内容
RACTuple *tuple3 = [RACTuple tupleWithObjectsFromArray:@[@"1",@"2",@"3"]];
// 添加某个值
[tuple tupleByAddingObject:@"123"];
NSLog(@"tupleByAddingObject = %@", tuple);
// 根据下标获取
NSLog(@"tuple objectAtIndex:2 = %@",[tuple objectAtIndex:2]);
// 取出所有的值
NSLog(@"[tuple2 allObjects] = %@",[tuple2 allObjects]);
NSLog(@"元组-->%@", tuple3[0]);
NSLog(@"第一个元素-->%@", [tuple3 first]);
NSLog(@"最后一个元素-->%@", [tuple3 last]);
}
其内部也实现了一些方法,在例子中都有展示。
- RACCommand类的使用
这个类比较的复杂,我准备后面单独的拿出详细的研究一下。这里就简单的介绍一下使用了。
//RACCommand
-(void)RACCommand{
/*
command 命令
可以监听信号的状态等,可以在订阅/执行(即 excute:方法)时传递参数。因此当需要向信号传递参数的时候,RACComand 更好用
RACCommand 包含了一个 executionSignal 的信号,这个信号是对用户透明的,它是自动创建的,由 RACCommand 进行管理。许多资料中把它称之为信号中的信号,是因为这个信号会发送其它信号——即 RACCommand 在初始化的 signalBlock 中创建(return)的信号。这个信号是 RACCommand 创建时由我们创建的,一般是用于处理一些异步操作,比如网络请求等。
RACCommand 的另一个优点了,它可以监听 RACCommand 自身的执行状态,比如开始、进行中、完成、错误等。用 RACSignal 可以监听到完成(complete)、错误(error)、进行中(next)。但开始就无法实现了.
*/
//NSString *input = @"执行";
RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
// execute发送的值
NSLog(@"input = %@",input);
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// RACSignal 发出信号
[subscriber sendNext:@"RACSignal : 我是谁,我在哪?"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
// block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
// 执行完Block后,当前信号就不再被订阅了。
// 信号销毁的时候 会执行这个闭包
// 用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
// 使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。
NSLog(@"signal1销毁了");
}];
}];
}];
//订阅外层信号(即 executionSignals)。外层信号在订阅或执行(即 execute: )时发送。因此我们可以将它视作请求即将开始之前的信号;
//订阅内层信号,因为内层信号由外层信号(executionSignals)作为数据发送(sendNext:),而发送的数据一般是作为 subcribeNext:时的 block 的参数来接收的,因此在这个块中,块的参数就是内层信号。这样我们就可以订阅内层信号了
// executionSignals就是用来发送信号的信号源,需要注意的是这个方法一定要在执行execute方法之前,否则就不起作用了
[command.executionSignals subscribeNext:^(RACSignal *_Nullable x) {
[x subscribeNext:^(id _Nullable x) {
NSLog(@"executionSignals-->subscribeNext-->%@",x);
}];
NSLog(@"executionSignals - x = %@",x);
}];
/*
获取信号中信号最近发出信号,订阅最近发出的信号
注意switchToLatest:只能用于信号中的信号
*/
[[command.executionSignals switchToLatest] subscribeNext:^(id _Nullable x) {
NSLog(@"switchToLatest-->%@",x);
}];
// 写法二 用点语法
// [command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
// <#code#>
// }];
// 监听信号是否执行完毕
[[command.executing skip:1] subscribeNext:^(id _Nullable x) {
NSLog(@"executing-->%@",x);
if ([x boolValue]) {
NSLog(@"正在执行");
}else{
NSLog(@"执行完毕");
}
}];
//RACCommand 比较特殊的一点是 error 信号需要在 errors 中订阅,而不能在 executionSignals 中订阅。在这里我们订阅了 errors 信号,并修改 data、error 和 requestStatus 属性值。
[command.errors subscribeNext:^(id _Nullable x) {
NSLog(@"errors-->%@",x);
}];
//开始执行
[command execute:@"起飞了"];
}
简单来说就是对信号进行管理和监听。
2. RAC定义的常用方法
- 按钮事件通过RAC来实现
// 点击事件用RAC实现
- (void)RACButton_targetAction {
// 按钮的点击事件
[[self.testButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"RAC按钮点击了");
NSLog(@"%@",x);
}];
// tap 点击事件
self.testLable.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
[self.testLable addGestureRecognizer:tap];
[tap.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
//点击事件响应的逻辑
NSLog(@"RAC的Label点击了");
NSLog(@"%@",x);
}];
}
- KVO的实现
- (void)RAC_KVO {
//1
[RACObserve(self.testLable, text) subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
//2
[RACObserve(self.testTextField, text) subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
} error:^(NSError * _Nullable error) {
NSLog(@"%@",error);
} completed:^{
NSLog(@"success");
}];
}
- RAC实现代理
// RAC实现代理
- (void)RACTextFieldDelegate {
[[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"textField delegate == %@",x);
}];
self.testTextField.delegate = self;
}
- RAC 实现通知
// RAC实现通知
- (void)RACNotification {
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidHideNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"%@",x);
}];
}
- RAC实现定时
- (void)RACTimer {
//主线程中每两秒执行一次
[[RACSignal interval:2.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"%@",x);
}];
//创建一个新线程
[[RACSignal interval:1 onScheduler:[RACScheduler schedulerWithPriority:(RACSchedulerPriorityHigh) name:@" com.ReactiveCocoa.RACScheduler.mainThreadScheduler"]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"%@",[NSThread currentThread]);
}];
}
- RAC 实现遍历
- (void)RACSequence {
//遍历数组
NSArray *racAry = @[@"rac1",@"rac2",@"rac3"];
[racAry.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
//遍历字典
NSDictionary *dict = @{@"name":@"dragon",@"type":@"fire",@"age":@"1000"};
[dict.rac_sequence.signal subscribeNext:^(id _Nullable x) {
RACTwoTuple *tuple = (RACTwoTuple *)x;
NSLog(@"key == %@, value == %@",tuple[0],tuple[1]);
}];
}
3. 相关函数的使用
- 映射相关
映射在项目中经常会被用到,映射方法是将原信号中的内容映射成新的指定内容。
通过对比,从map的实现方法中可以看出是基于flattenMap方法的一层封装,但同时又有不同之处。- flattenMap 映射
flattenMap作用:把原信号的内容映射成一个新信号,并return返回给一个RACStream类型数据。实际上是根据前一个信号传递进来的参数重新建立了一个信号,这个参数,可能会在创建信号的时候用到,也有可能用不到。
- flattenMap 映射
- (void)FlattenMap {
// flattenMap
[[self.testTextField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
//自定义返回内容
return [RACReturnSignal return:[NSString stringWithFormat:@"flattenMap 自定义了返回信号:%@",value]];
}] subscribeNext:^(id _Nullable x) {
NSLog(@"x = %@",x);
NSLog(@"%@",NSStringFromClass([x class]));
}];
}
- Map 映射
map作用:是将原信号的值自定义为新的值,不需要再返回RACStream类型,value为源信号的内容,将value处理好的内容直接返回即可。map方法将会创建一个一模一样的信号,只修改其value。
// map 和 flattenMap 映射
- (void)map {
[[self.testTextField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
return [NSString stringWithFormat:@"map 自定义了返回信号:%@",value];
}] subscribeNext:^(id _Nullable x) {
NSLog(@"x = %@",x);
NSLog(@"%@",NSStringFromClass([x class]));
}];
}
同样作为映射命令,在实际开发过程中,如果使用map命令,则block代码块中return的是对象类型;而flattenMap命令block代码块中return的是一个新的信号。
- 过滤相关
- skip实现过滤
忽略前几个信号值
- skip实现过滤
-(void)RACSkip{
RACSubject *subject = [RACSubject subject];
[[subject skip:1] subscribeNext:^(id _Nullable x) {
NSLog(@"skip = %@",x);
}];
[subject sendNext:@"12"];
[subject sendNext:@"123"];
[subject sendNext:@"1234"];
}
得到的信号值 只有 123 和 1234,在开发中经常会用它来过滤第一个原始值。
- filter实现过滤
通过return一个BOOL值来判断是否过滤掉信号。
- (void)RACfilter {
@weakify(self);
[[self.testTextField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
//过滤判断条件
@strongify(self)
if (self.testTextField.text.length >= 6) {
self.testTextField.text = [self.testTextField.text substringToIndex:6];
self.testLable.text = @"已经到6位了";
self.testLable.textColor = [UIColor redColor];
}else{
self.testLable.text = @"最多输入6位数";
self.testLable.textColor = [UIColor greenColor];
}
return value.length <= 6;
}] subscribeNext:^(NSString * _Nullable x) {
//订阅逻辑区域
NSLog(@"filter过滤后的订阅内容:%@",x);
}];
}
- ignore实现过滤
// ignore实现过滤
- (void)ignoreValue {
[[self.testTextField.rac_textSignal ignoreValues] subscribeNext:^(id _Nullable x) {
//将self.testTextField的所有textSignal全部过滤掉
NSLog(@"过滤后%@",x);
}];
[[self.testTextField.rac_textSignal ignore:@"1"] subscribeNext:^(id _Nullable x) {
//将self.testTextField的textSignal中字符串为指定条件的信号过滤掉
NSLog(@"过滤 1 后%@",x);
}];
}
- distinctUntilChanged 实现过滤
判断当前信号的值跟上一次的值相同,若相同时将不会收到订阅信号
- (void)distinctUntilChanged {
// 第一种:不做distinctUntilChanged限制的
// 1. 创建
RACSubject *subject = [RACSubject subject];
// 2.1订阅
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"RACSubjectTest x = %@",x);
} error:^(NSError * _Nullable error) {
NSLog(@"%@",error);
} completed:^{
NSLog(@"11111");
}];
//3.发送信号
[subject sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"];
[subject sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"];
//第二种:通过 distinctUntilChanged 方法来 判断前面信号的值是否和当前的相同,如果相同就不会去接受信号
// 1. 创建
RACSubject *subject2 = [RACSubject subject];
// 2.1订阅
[[subject2 distinctUntilChanged] subscribeNext:^(id _Nullable x) {
NSLog(@"RACSubjectTest distinctUntilChanged x = %@",x);
}];
// 2.2错误
[subject subscribeError:^(NSError * _Nullable error) {
NSLog(@"error = %@",error);
}];
// 3.发送
[subject2 sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"]; // 1
[subject2 sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"]; // 2
[subject2 sendNext:@"信号🍺🍺🍺"]; // 3
}
打印的结果只有 1 、3
- 链接、合并信号相关
- concat连接信号
特点:concat组合信号,让信号按照顺序去执行。
使用场景:假如我们现在有这么一个需求:有两个网络请求,我们需要先请求第一个,拿到第一个返回的数据后,再去执行第二个网络请求。这时,我们就可以用到concat了。
注意点:当你使用concat 连接信号的时候,第一个信号一定要执行sendCompleted方法,不然永远不用执行第二个信号。
- concat连接信号
-(void)RACConcat{
// 创建信号1
RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"A123"];
//一定要执行sendCompleted,不然永远都不会执行请求B
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"A 销毁了");
}];
}];
[signalA subscribeNext:^(id _Nullable x) {
NSLog(@"A = %@",x);
}];
RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"B1234"];
//一定要执行sendCompleted,不然永远都不会执行请求B
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"B 销毁了");
}];
}];
[signalB subscribeNext:^(id _Nullable x) {
NSLog(@"B = %@",x);
}];
// concat:按照顺序去连接,先执行请求A,再去执行请求B
RACSignal *concatSignal = [signalA concat:signalB];
// 订阅组合信号
[concatSignal subscribeNext:^(id _Nullable x) {
// 既能拿到A信号的值,又能拿到B信号的值
NSLog(@"A和B = %@",x);
}];
}
/*
2019-09-04 12:06:50.773126+0800 ReactiveObjc[3070:463032] A = A123
2019-09-04 12:06:50.773196+0800 ReactiveObjc[3070:463032] A 销毁了
2019-09-04 12:06:50.773226+0800 ReactiveObjc[3070:463032] B = B1234
2019-09-04 12:06:50.773241+0800 ReactiveObjc[3070:463032] B 销毁了
2019-09-04 12:06:50.773550+0800 ReactiveObjc[3070:463032] A和B = A123
2019-09-04 12:06:50.773572+0800 ReactiveObjc[3070:463032] A和B = B1234
2019-09-04 12:06:50.773585+0800 ReactiveObjc[3070:463032] B 销毁了
2019-09-04 12:06:50.773595+0800 ReactiveObjc[3070:463032] A 销毁了
*/
- then 连接信号
then也是用于连接两个信号,当第一个信号完成,才会连接then返回的信号,then底层也是使用了concat
实现
-(void)RACThen{
RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 发送信息
[subscriber sendNext:@"signalA"];
// 发送完成,不然接受不到B的数据
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"signalA dealloc");
}];
}];
[signalA subscribeNext:^(id _Nullable x) {
NSLog(@"signalA = %@",x);
}];
RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 发送信息
[subscriber sendNext:@"signalB"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"signalB dealloc");
}];
}];
[signalB subscribeNext:^(id _Nullable x) {
NSLog(@"signalB = %@",x);
}];
// 利用then组合信号
// then:会忽略掉 A 信号的所有值 之后再去接收 B 的值
RACSignal *thenSignal = [signalA then:^RACSignal * _Nonnull{
// 返回 B 的信号
return signalB;
}];
[thenSignal subscribeNext:^(id _Nullable x) {
NSLog(@" signalB = %@",x);
}];
/*
2019-09-04 13:57:00.316571+0800 ReactiveObjc[3551:500030] signalA = signalA
2019-09-04 13:57:00.316647+0800 ReactiveObjc[3551:500030] signalA dealloc
2019-09-04 13:57:00.316692+0800 ReactiveObjc[3551:500030] signalB = signalB
2019-09-04 13:57:00.316714+0800 ReactiveObjc[3551:500030] signalB dealloc
2019-09-04 13:57:00.318309+0800 ReactiveObjc[3551:500030] x = signalB
2019-09-04 13:57:00.318333+0800 ReactiveObjc[3551:500030] signalB dealloc
2019-09-04 13:57:00.318349+0800 ReactiveObjc[3551:500030] signalA dealloc
*/
}
- merge 合并信号
merge把两个信号合并为一个信号,任何一个信号有新值的时候就会调用
.
-(void)RACMerge{
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACSubject subject];
RACSignal *merge = [subjectA merge:subjectB];
// 订阅信号
[merge subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
// 任何一个信号有新的值的时候就会调用,谁先发请求谁先响应
[subjectA sendNext:@"发送A请求"];
[subjectB sendNext:@"发送B请求"];
/*
2019-09-04 14:13:10.027433+0800 ReactiveObjc[3574:503914] 发送A请求
2019-09-04 14:13:10.027487+0800 ReactiveObjc[3574:503914] 发送B请求
*/
}
- ZipWith 合并信号
把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,并且把两个信号的内容合并成一个元组,才会触发事件
。
当一个界面有多个网络请求时,要等所有请求完成才更新UI,这时我们就可以用zipWith
注意:只有当两个请求都发送请求之后才会触发回调事件
-(void)RACZipWith{
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACSubject subject];
RACSignal *zipWith = [subjectA zipWith:subjectB];
[zipWith subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
// RACTwoTupl继承自 RACTuple RACTuple 类似于数组
[zipWith subscribeNext:^(RACTwoTuple * _Nullable x) {
NSLog(@"%@",[x objectAtIndex:0]);
}];
//只有当两个请求都发送请求之后才会触发回调事件
[subjectB sendNext:@"send B request"];
[subjectA sendNext:@"send A request"];
//上面我们明明就是先调用 signalB 的sendNext 方法,但回调打印的却是signalA在前面。这是为什么?因为我们在组合信号的时候 [signalA zipWith:signalB],我们把signalA放前面,所以回调的时候它也会放在前面。
/*
<RACTwoTuple: 0x28252bb20> (
"send A request",
"send B request"
)
*/
}
- combineLatest 和 Reduce 合并信号
这我感觉也是项目中合并信号用到最多的一个函数式了
🌰
-(void)combineLatestAndReduce{
CGFloat width = self.view.bounds.size.width;
//账号
UITextField *tf1 = [[UITextField alloc]initWithFrame:CGRectMake(width / 2.0 - 100, 380, 200, 40)];
tf1.placeholder = @"请输入账号";
tf1.backgroundColor = [UIColor colorWithRed:0.0f green:0.5f blue:0.8f alpha:1.0f];
[self.view addSubview:tf1];
//密码
UITextField *tf2 = [[UITextField alloc]initWithFrame:CGRectMake(width / 2.0 - 100, 450, 200, 40)];
tf2.placeholder = @"请输入密码";
tf2.backgroundColor = [UIColor colorWithRed:0.0f green:0.5f blue:0.8f alpha:1.0f];
[self.view addSubview:tf2];
//登陆按钮
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(width / 2.0 - 100, 580, 200, 40);
[button setTitle:@"登录" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:15];
[button setBackgroundColor:[UIColor greenColor]];
//@weakify(self);
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
//@strongify(self);
NSLog(@"登录");
}];
[self.view addSubview:button];
//组合信号
// 第一个参数:组合哪些信号
// 第二个参数:reduce 聚合
// 注意,reduce是一个block,它看起来好像没参数,但其实它的参数是由我们自己来决定的,我们可以自己去修改
// reduce 参数: 跟组合的信号有关,一一对应,你有几个信号,就有几个参数,所以我们在下面加上(NSString *account, NSString *pwd)
// reduce 参数类型: 组合的信号发送的是什么类型,参数就是什么类型
RACSignal *combineSignal = [RACSignal combineLatest:@[tf1.rac_textSignal,tf2.rac_textSignal] reduce:^id _Nullable(NSString *account, NSString *pwd){
// block调用: 只要任意一个源信号发送内容就会调用,组合成一个新的值
return @(account.length && pwd.length);
}];
// [combineSignal subscribeNext:^(id _Nullable x) {
// NSLog(@"%@",x);
// button.enabled = [x boolValue];
// }];
//上面的写法其实可以换成下面这种,更简单明了
// RAC 用于给某个对象的某个属性绑定
RAC(button,enabled) = combineSignal;
}
到这里,RAC的一些基本使用就介绍完了,当然知其然要知其所以然,阅读源码,了解人家实现的思路才是王道。各位骚年们,我们一起努力吧!!⛽️