RAC
搜索示例:
部分代码:
/*
第一步:[self.signInService checkSateSignal] : 是否允许搜索等判断
第二步:then直到 判断完成 ,返回self.username.rac_textSignal(输入信号)
第三步:(条件过滤1)filter:校验输入信号是否合法
第四步:(条件过滤2)throttle:连续输入信息时不请求,输入至少停顿(例如500毫
秒)才执行搜索,防止过于频繁
第五步:flattenMap:上面合法时,调用搜索请求接口,并将返回结果信号中的值
(Map直接返回信号本身)
第六步:回到主线程[RACScheduler mainThreadScheduler]:请求完成后回到主
线程,再进行下一步返回
第七步:subscribeNext:error: 请求数据对象(显示列表界面)或请求失败
*/
- (void)demo1 {
@weakify(self)
[[[[[[[self.signInService checkSateSignal] then:^RACSignal *{ //条件判断
(checkSateSignal 成功后执行then)
@strongify(self)
return self.username.rac_textSignal; //输入信号
}]
filter:^BOOL(NSString *value) {
@strongify(self)
return [self isValidSearchText:value]; //过滤输入信息是否符合
}]
throttle:0.5] //节流: 当搜索内容改变时不是立马搜索,而是至少有一个时间
间隔
flattenMap:^RACStream *(NSString *value) {//flattenMap 将信号进行合
并处理后返回一个新的信号
return [self.signInService signInSignalWithTxt:value]; //发送请求
}]
deliverOn:[RACScheduler mainThreadScheduler]] //回到主线程
subscribeNext:^(id data) { //返回结果
@strongify(self)
NSDictionary *dataDic = data;
NSLog(@"请求数据:%@",dataDic);
//显示列表界面
self.desc.hidden = YES;
self.tableView.hidden = NO;
self.dataSource = [dataDic[@"list"] mutableCopy];
[self.tableView reloadData];
} error:^(NSError *error) { //失败
NSLog(@"请求失败:%@",error.domain);
}];
}
//输入是否合法
- (BOOL)isValidSearchText:(NSString *)text {
if (text.length > 3) {
return YES;
}
return NO;
}
简单登录
/**
:点击按钮时:
点击事件->创建信号
doNext:是直接跟在按钮点击事件的后面。而且doNext: block并没有返回值。
因为它是附加操作,并不改变事件本身。
doNext不会影响数据本身,也没有返回值,是一个附件功能:告诉你点击了
flattenMap: 信号中的信号
当管道信号量是[信号中的信号]时,解决的方法很简单,只需要把map操作改成
flattenMap就可以了:
*/
- (void)demo7 {
//按钮否可以操作
[self demo6];
//可操作:将按钮点击事件创建一个信号,返回值默认是按钮本身UIButton
RACSignal *clickSignal = [self.login
rac_signalForControlEvents:UIControlEventTouchUpInside];
[[[clickSignal
doNext:^(id x) { //doNext不会影响数据本身,也没有返回值,是一个附件功
能:告诉你点击了
self.login.enabled = NO;//点击按钮后不可使用
[self.login setTitle:@"登录中..." forState:0];
[SVProgressHUD showWithStatus:@"登录中..."];
}]
flattenMap:^id(id x) {//信号中的信号
return [self signInSignal];//点击按钮发送异步请求
}]
subscribeNext:^(NSNumber *signedIn) {
self.login.enabled = YES;//执行完成后可点击
[self.login setTitle:@"登录" forState:0];
BOOL success = [signedIn boolValue];
[SVProgressHUD showSuccessWithStatus:@"登录成功"];
NSLog(success ? @"登录成功" : @"登录失败");
} error:^(NSError *error) {
NSLog(@"失败");
[SVProgressHUD showSuccessWithStatus:@"登录失败"];
} completed:^{
//结束
}];
}
简单使用步骤:
RACSiganl 信号类
1、创建信号
2、订阅信号
3、发送信号
4、取消订阅(图中未标明)
类比
整个过程就像是:
1.创建信号 :
创建一个对象,包含多个block方法 , 可以用来保存一个block (复制多分)
2.发送信号 :
请求成功后,使用对象的block来返回数据
3.订阅信号 :
调用对象block方法, block实现,获取返回的数据,并进行下一步处理
4.取消订阅 :
对象内部一些处理,内存释放, 好比 delegate = nil
简单理解就是将block比作信号 , 对其进行一系列封装
1.创建信号
创建信号本质就是创建了一个 RACDynamicSignal 类型的信号,并将传入的代
码块保存起来,留待以后调用
2.订阅信号
就是调用信号对象 Next 、Error 、 completed 等等方法
这些方法的内部都使用了一个类RACSubscriber(我们称为订阅者,
RACPassthroughSubscriber 类型) ,来处理相应返回(保存block,等待请求成功
后返回数据时进行处理)
3.发送信号
发送信号就是: 请求数据后执行相应block来传递数据 (或者状态数据等)
但是不同调用函数逻辑又有区别 :
sendNext :
一直传递数据 (类似Prograss 进度条)
sendCompleted :
先取消该订阅, 再传递一次数据(后面不能再使用, 已结束)
sendError :
同sendCompleted, 只是在错误状态下传递error数据
4.取消订阅信号
这个很好理解,就是不在接收数据了, 取消监听
取消订阅就是把订阅信号获得的disposable 进行dispose即可在调度器调度该部
分代码之前禁止调用
总结1 :
上面4个步骤简单介绍了信号RACSignal的使用
我们可以把RACSignal 类比 代理delegate功能 来理解
其他类大都是在RACSignal基础上进行封装,完成更多的功能
RACSubject 类的使用 :
1. RACSubject是在RACSignal基础上增加了功能, 用数组存放多个信号, 这样就
能接收多次订阅等等
2. RACSubject同样也是完成创建,订阅,发送,取消 信号的功能
3. RACSubject接收一次订阅,就好比是代理delegate功能
一对一
4. RACSubject接收多次订阅,好比是iOS的通知Notice 功能
一对多
RACCommand 类的使用 :
RACCommand 封装一些功能,使得数据可以双向流通 ,不仅传递信号,还能接收对
象的状态信号,并进行一些额外的处理
RACCommand 可以发送信号时,传递一些参数, 同样可以完成操作前的条件判断
应用:
1. UIButton的enable属性,command开始执行是NO, 执行完了变成YES , 这样避
免重复点击
button.rac_command
2. 登录按钮, 登录判断
3. 网络请求
登录
@weakify(self)
RAC(self.viewModel, userName) = self.userNameTF.rac_textSignal;
RAC(self.viewModel, password) = self.passwordTF.rac_textSignal;
self.loginBtn.rac_command = self.viewModel.loginCommand;
[[self.viewModel.loginCommand executionSignals]
subscribeNext:^(RACSignal *x) {
@strongify(self)
self.juhuaView.hidden = NO;
[x subscribeNext:^(NSString *x) {
self.juhuaView.hidden = YES;
NSLog(@"%@",x);
}];
}];
总结2 :
RACSubject 和 RACCommand 都是功能封装, 包括其他类
bind : 核心方法
1. bind操作方法实质上就是生成新的绑定信号,利用returnSignal作为中间信号
来改变源数据生成新的数据并执行新绑定信号的nextBlock代码块
2. 其他操作方法大都是都是以bind方法为核心的进行封装,来完成不同的需求,比
如map, flattenMap 等等
map 映射方法
本质是bind返回的信号:
在 map 方法中,返回的是 flattenMap 方法生成的信号,而 flattenMap 内部返
回的是 bind 方法返回的信号
map 映射方法的实质就是 bind 方法和 signal 深度封装,实际的信号流的流程
还是以 bind为核心
其他类似
总结3 :
很多方法的实质就是 bind 方法和 signal 深度封装
其他方法:
skip
-(void)skip{
//skip传入n跳过前面n个值
RACSubject * subject = [RACSubject subject];
[[subject skip:2] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subject sendNext:@1]; //跳过
[subject sendNext:@2];//跳过
[subject sendNext:@2];//执行
}
take
RACSubject * subject = [RACSubject subject];
//限制取值次数 - 取2次结束
[[subject take:2]subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subject sendNext:@1];//执行
[subject sendNext:@2];//执行
[subject sendNext:@3];//跳过
takeLast
RACSubject * subject = [RACSubject subject];
//取的是最后几个值
[[subject takeLast:2] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subject sendNext:@1];//无效
[subject sendNext:@2];//有效
[subject sendNext:@3]; //有效
[subject sendCompleted];//不写会取不到最后要的值
takeUntil
RACSubject * subject = [RACSubject subject];
RACSubject * subject2 = [RACSubject subject];
[[subject takeUntil:subject2] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject2 sendNext:@3];//调用后不会收到下边发送的内容
[subject2 sendNext:@4];
}
ignore
// ignore 忽略一些值
// ignoreValue 忽略所有的值
RACSubject * subject = [RACSubject subject];
RACSignal * ignoreSignal = [subject ignore:@2]; //忽略2
[ignoreSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subject sendNext:@2]; //被忽略
then
等待A信号结束再执行B,但是A的信号被忽略
//创建信号A, 必须执行sendCompleted,不然信号B没法执行
RACSignal * signalA = [RACSignal createSignal:^RACDisposable
*(id<RACSubscriber> subscriber) {
NSLog(@“- 发送上部分请求- afn");
[subscriber sendNext:@"上部分数据”];//该信号被忽略
[subscriber sendCompleted];
return nil;
}];
//创建信号B, 必须执行sendCompleted
RACSignal * signalB = [RACSignal createSignal:^RACDisposable
*(id<RACSubscriber> subscriber) {
NSLog(@"- 发送下部分请求- afn");
[subscriber sendNext:@"下部分数据"];
[subscriber sendCompleted];
return nil;
}];
// 创建组合信号:signalA执行结束后执行B,但是A的信号值不返回
// then;忽略掉第一个信号的所有值
RACSignal * thenSignal = [signalA then:^RACSignal *{
return signalB; // 只会返回signalB的信号
}];
[thenSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
打印结果 :
- 发送上部分请求- afn
- 发送下部分请求- afn
下部分数据
concat
等待A信号结束再执行B,A的信号不会被忽略,先返回A,再返回B
替换上面then代码为concat
//**-注意-**:concat,第一个信号必须要调用sendCompleted
RACSignal * concatSignal = [signalA concat:signalB];
[concatSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
打印结果 :
- 发送上部分请求- afn
上部分数据
- 发送下部分请求- afn
下部分数据
distinctUntilChanged
RACSubject * subject = [RACSubject subject];
//处理解决相同值没必要多次被订阅
[[subject distinctUntilChanged] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@2];//不会被订阅
doNext
flattenMap
zipWith
//zipWith:把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,
并且把两个信号的内容合并成一个元祖,才会触发压缩流的next事件。
//使用场:当一个界面多个请求的时候,要等所有请求完成才更新UI
RACSubject * signalA = [RACSubject subject];
RACSubject * signalB = [RACSubject subject];
RACSignal * zipSignal = [signalA zipWith:signalB]; //跟压缩的顺序有关 先A
后B
//等所有信号都发送内容的时候才会调用
[zipSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//signalB 结束后调用
[signalA sendNext:@1];
[signalB sendNext:@2];
merge
RACSubject * signalA = [RACSubject subject];
RACSubject * signalB = [RACSubject subject];
//组合信号:多个信号合并成一个信号,任何一个信号有新值就会调用 ,任何一
个信号请求完成都会被订阅到
RACSignal * mergeSignal = [signalA merge:signalB];
[mergeSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 发送信号 - 交换位置则数据结果顺序也会交换
[signalB sendNext:@2];
[signalA sendNext:@1];
map
返回合成的信号
combineLatest: reduce:
reduce 返回多信号元组(A,B)
https://www.jianshu.com/p/c2fba4f1e32e
filter
[[textField.rac_textSignal filter:^BOOL(id value) {
return [value length] > 5;//返回值就是过滤条件 满足才能获取到
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];