初级篇简单的介绍了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:把元祖里的值分别都取出来,然后对这些值做一些操作,再合成一个值返回出去。
上面这段代码,可用于登录时的操作,监听两个文本框的值,如果都有值,返回的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的了解有多少,可以去美团看技术博客:传送门