RAC的組合信號有以下幾種
- (RACSignal *)concat:(RACSignal *)signal
- (RACSignal *)then:(RACSignal * (^)(void))block
- (RACSignal *)merge:(RACSignal *)signal
- (RACSignal *)zipWith:(RACSignal *)signal
+ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock
1. concat
concat組合信號,讓信號按照順序去執(zhí)行。
假如我們現(xiàn)在有這么一個(gè)需求:有兩個(gè)網(wǎng)絡(luò)請求,我們需要先請求第一個(gè),拿到第一個(gè)返回的數(shù)據(jù)后,再去執(zhí)行第二個(gè)網(wǎng)絡(luò)請求。這時(shí),我們就可以用到concat了。
// 創(chuàng)建信號A
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"發(fā)送請求A");
[subscriber sendNext:@"發(fā)送請求A返回的數(shù)據(jù)”];
//一定要執(zhí)行sendCompleted,不然永遠(yuǎn)都不會(huì)執(zhí)行請求B
[subscriber sendCompleted];
return nil;
}];
// 創(chuàng)建信號B
RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"發(fā)送請求B");
[subscriber sendNext:@"發(fā)送請求B返回的數(shù)據(jù)"];
[subscriber sendCompleted];
return nil;
}];
// concat:按照順序去連接,先執(zhí)行請求A,再去執(zhí)行請求B
RACSignal *concatSignal = [signal concat:signalB];
// 訂閱組合信號
[concatSignal subscribeNext:^(id _Nullable x) {
// 既能拿到A信號的值,又能拿到B信號的值
NSLog(@"%@",x);
}];
打印結(jié)果:
2017-06-11 20:28:20.632 RACDemo[2497:1073327] 發(fā)送請求A
2017-06-11 20:28:20.632 RACDemo[2497:1073327] 發(fā)送請求A返回的數(shù)據(jù)
2017-06-11 20:28:20.633 RACDemo[2497:1073327] 發(fā)送請求B
2017-06-11 20:28:20.633 RACDemo[2497:1073327] 發(fā)送請求B返回的數(shù)據(jù)
上面有個(gè)要注意的點(diǎn):
當(dāng)你使用concat 連接信號的時(shí)候,第一個(gè)信號一定要執(zhí)行sendCompleted方法,不然永遠(yuǎn)不用執(zhí)行第二個(gè)信號。
2. then
then也是用于連接連個(gè)信號,當(dāng)?shù)谝粋€(gè)信號完成,才會(huì)連接then返回的信號,then底層也是使用了concat實(shí)現(xiàn)。
例子
// 創(chuàng)建信號A
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"發(fā)送請求A");
//其實(shí)寫這句是沒用的,因?yàn)橛胻hen連接信號,會(huì)自動(dòng)忽略第一個(gè)信號的所有值
[subscriber sendNext:@"發(fā)送請求A返回的數(shù)據(jù)”];
[subscriber sendCompleted];
return nil;
}];
// 創(chuàng)建信號B
RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"發(fā)送請求B");
[subscriber sendNext:@"發(fā)送請求B返回的數(shù)據(jù)"];
[subscriber sendCompleted];
return nil;
}];
// 利用then組合信號
// then:會(huì)忽略掉第一個(gè)信號的所有值
RACSignal *thenSignal = [signal then:^RACSignal * _Nonnull{
// 返回信號就是需要組合的信號
return signalB;
}];
[thenSignal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
打印結(jié)果
2017-06-05 19:49:40.045 RACDemo[4615:451292] 發(fā)送請求A
2017-06-05 19:49:40.046 RACDemo[4615:451292] 發(fā)送請求B
2017-06-05 19:49:40.046 RACDemo[4615:451292] 發(fā)送請求B返回的數(shù)據(jù)
then很奇葩,因?yàn)樗鼤?huì)自動(dòng)忽略掉第一個(gè)信號發(fā)送的值,只接收第二個(gè)信號發(fā)送的值。在項(xiàng)目中,基本也很少會(huì)遇見這種需求。更多使用的是concat。
3.merge
merge把兩個(gè)信號合并為一個(gè)信號,任何一個(gè)信號有新值的時(shí)候就會(huì)調(diào)用。
例子
// 創(chuàng)建信號A
RACSubject *signalA = [RACSubject subject];
// 創(chuàng)建信號B
RACSubject *signalB = [RACSubject subject];
// 組合
RACSignal *mergeSignal = [signalA merge:signalB];
// 訂閱信號
[mergeSignal subscribeNext:^(id _Nullable x) {
// 任意一個(gè)信號發(fā)送內(nèi)容都會(huì)來到這個(gè)block里面。
NSLog(@"%@",x);
}];
[signalB sendNext:@"發(fā)送B請求"];
[signalA sendNext:@"發(fā)送A請求”];
打印結(jié)果
2017-06-12 00:13:02.364 RACDemo[3342:1458662] 發(fā)送B請求
2017-06-12 00:13:05.244 RACDemo[3342:1458662] 發(fā)送A請求
4.zipWith
把兩個(gè)信號壓縮成一個(gè)信號,只有當(dāng)兩個(gè)信號同時(shí)發(fā)出信號內(nèi)容時(shí),并且把兩個(gè)信號的內(nèi)容合并成一個(gè)元組,才會(huì)觸發(fā)事件。當(dāng)一個(gè)界面有多個(gè)網(wǎng)絡(luò)請求時(shí),要等所有請求完成才更新UI,這時(shí)我們就可以用zipWith
例子
// 創(chuàng)建信號A
RACSubject *signalA = [RACSubject subject];
// 創(chuàng)建信號B
RACSubject *signalB = [RACSubject subject];
RACSignal *zipSignal = [signalA zipWith:signalB];
// 訂閱信號
[zipSignal subscribeNext:^(id _Nullable x) {
// block調(diào)用時(shí)刻:當(dāng)signalA 和 signalB 都發(fā)送信號的時(shí)候,就會(huì)調(diào)用
// x 被包裝成一個(gè)元組
NSLog(@"%@",x);
}];
[signalB sendNext:@"send B request"];
[signalA sendNext:@"send A request"];
打印結(jié)果:
<RACTwoTuple: 0x60000001ae90> (
"send A request",
"send B request"
)
這里我們仔細(xì)觀察,
1.zipWith 一定要兩個(gè)信號都執(zhí)行完成 sendNext 操作,才會(huì)觸發(fā)回調(diào)事件。
2.上面我們明明就是先調(diào)用 signalB 的sendNext 方法,但回調(diào)打印的卻是signalA在前面。這是為什么?因?yàn)槲覀冊诮M合信號的時(shí)候 [signalA zipWith:signalB],我們把signalA放前面,所以回調(diào)的時(shí)候它也會(huì)放在前面。
5.combineLatest
combineLatest 將多個(gè)信號合并起來,拿到各個(gè)信號的最新的值,每個(gè)合并的signal至少都有過一次sendNext,才會(huì)觸發(fā)合并的信號,底層也是用 zipWith實(shí)現(xiàn)的。 一般拿來跟 reduce 一起使用。
我們直接來看一個(gè)常見的登陸例子。
需求:一個(gè)賬號輸入框 和一個(gè)密碼 輸入框,一個(gè)登陸按鈕。當(dāng)賬號長度和密碼長度都大于0的時(shí)候,登陸按鈕可以點(diǎn)擊,否則,不可以點(diǎn)擊。
CGFloat width = self.view.bounds.size.width;
//賬號
UITextField *tf1 = [[UITextField alloc]initWithFrame:CGRectMake(width / 2.0 - 100, 80, 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, 150, 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, 280, 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];
//組合信號
// 第一個(gè)參數(shù):組合哪些信號
// 第二個(gè)參數(shù):reduce 聚合
// 注意,reduce是一個(gè)block,它看起來好像沒參數(shù),但其實(shí)它的參數(shù)是由我們自己來決定的,我們可以自己去修改
// reduce 參數(shù): 跟組合的信號有關(guān),一一對應(yīng),你有幾個(gè)信號,就有幾個(gè)參數(shù),所以我們在下面加上(NSString *account, NSString *pwd)
// reduce 參數(shù)類型: 組合的信號發(fā)送的是什么類型,參數(shù)就是什么類型
RACSignal *combineSignal = [RACSignal combineLatest:@[tf1.rac_textSignal,tf2.rac_textSignal] reduce:^id _Nullable(NSString *account, NSString *pwd){
// block調(diào)用: 只要任意一個(gè)源信號發(fā)送內(nèi)容就會(huì)調(diào)用,組合成一個(gè)新的值
return @(account.length && pwd.length);
}];
[combineSignal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
button.enabled = [x boolValue];
}];
combineLatest 大多數(shù)都是和reduce一起使用,reduce使用時(shí)需要注意的就是參數(shù),上面已經(jīng)解釋的很清楚了,這里就不多余解釋了。
[combineSignal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
button.enabled = [x boolValue];
}];
//上面的寫法其實(shí)可以換成下面這種,更簡單明了
// RAC 用于給某個(gè)對象的某個(gè)屬性綁定
RAC(button,enabled) = combineSignal;