- 之前已經(jīng)有兩篇文章介紹了
ReactiveCocoa
的一些見解和用法, 這里也就不再作介紹了 - 其中第一篇文章介紹了ReactiveCocoa關(guān)于RACSingle的使用詳解
- 第二篇文章介紹了ReactiveCocoa之集合使用詳解
- 有興趣的可以先去看看這兩篇文章
- 接下來我們就著重介紹一些
Map
,concat
等高級(jí)用法
一. ReactiveCocoa常見操作介紹
1. ReactiveCocoa操作須知
- 所有的信號(hào)
RACSignal
都可以進(jìn)行操作處理,因?yàn)樗胁僮鞣椒ǘ级x在RACStream.h中,因此只要繼承RACStream就有了操作處理方法坪创。
2. ReactiveCocoa操作思想
- 運(yùn)用的是Hook(鉤子)思想瞎嬉,Hook是一種用于改變API(應(yīng)用程序編程接口:方法)執(zhí)行結(jié)果的技術(shù).
- Hook用處:截獲API調(diào)用的技術(shù)。
- Hook原理:在每次調(diào)用一個(gè)API返回結(jié)果之前扰肌,先執(zhí)行你自己的方法抛寝,改變結(jié)果的輸出
二. 高級(jí)操作
1. ReactiveCocoa核心方法bind
-
ReactiveCocoa
操作的核心方法是bind
(綁定),而且RAC中核心開發(fā)方式,也是綁定曙旭,之前的開發(fā)方式是賦值盗舰,而用RAC開發(fā),應(yīng)該把重心放在綁定桂躏,也就是可以在創(chuàng)建一個(gè)對(duì)象的時(shí)候钻趋,就綁定好以后想要做的事情,而不是等賦值之后在去做事情剂习。 - 在開發(fā)中很少使用bind方法蛮位,bind屬于RAC中的底層方法较沪,RAC已經(jīng)封裝了很多好用的其他方法,底層都是調(diào)用bind失仁,用法比bind簡(jiǎn)單.
- bind方法簡(jiǎn)單介紹和使用
- 需求: 監(jiān)聽文本框的內(nèi)容, 每次輸出的時(shí)候, 在內(nèi)容后面品尚字符串
"jun"
, 并顯示在label
上
- 需求: 監(jiān)聽文本框的內(nèi)容, 每次輸出的時(shí)候, 在內(nèi)容后面品尚字符串
方式一: 在返回結(jié)果后, 拼接字符串
@weakify(self)
[_textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
@strongify(self)
self.showLabel.text = [NSString stringWithFormat:@"%@+%@", x, @"jun"];
}];
方式二: 在返回結(jié)果前, 拼接字符串, 用bind方法操作
[[_textField.rac_textSignal bind:^RACSignalBindBlock _Nonnull{
return ^RACSignal *(id value, BOOL *stop){
return [RACReturnSignal return:[NSString stringWithFormat:@"輸出: %@", value]];
};
}] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
-
bind
方法介紹-
bind
方法參數(shù):需要傳入一個(gè)返回值是RACStreamBindBlock
的block
參數(shù) -
RACStreamBindBlock
是一個(gè)block
的類型尸曼,返回值是信號(hào),參數(shù)(value,stop)
萄焦,因此參數(shù)的block
返回值也是一個(gè)block
- 如下:
-
typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);
-
RACStreamBindBlock
:- 參數(shù)一(value): 表示接收到信號(hào)的原始值控轿,還沒做處理
- 參數(shù)二
*stop
: 用來控制綁定Block
,如果*stop
= yes,那么就會(huì)結(jié)束綁定拂封。 - 返回值:信號(hào)茬射,做好處理,在通過這個(gè)信號(hào)返回出去冒签,一般使用
RACReturnSignal
,需要手動(dòng)導(dǎo)入頭文件RACReturnSignal.h
@interface RACReturnSignal<__covariant ValueType> : RACSignal<ValueType>
+ (RACSignal<ValueType> *)return:(ValueType)value;
@end
- bind方法使用步驟:
- 傳入一個(gè)返回值
RACStreamBindBlock
的block
- 描述一個(gè)
RACStreamBindBlock
類型的bindBlock
作為block
的返回值在抛。 - 描述一個(gè)返回結(jié)果的信號(hào),作為
bindBlock
的返回值镣衡。 - 注意:在
bindBlock
中做信號(hào)結(jié)果的處理
- 傳入一個(gè)返回值
- bind底層實(shí)現(xiàn):
- 源信號(hào)調(diào)用bind,會(huì)重新創(chuàng)建一個(gè)綁定信號(hào)霜定。
- 當(dāng)綁定信號(hào)被訂閱,就會(huì)調(diào)用綁定信號(hào)中的
didSubscribe
廊鸥,生成一個(gè)bindingBlock
望浩。 - 當(dāng)源信號(hào)有內(nèi)容發(fā)出,就會(huì)把內(nèi)容傳遞到
bindingBlock
處理惰说,調(diào)用bindingBlock(value,stop)
- 調(diào)用
bindingBlock(value,stop)
磨德,會(huì)返回一個(gè)內(nèi)容處理完成的信號(hào)(RACReturnSignal)
。 - 訂閱
RACReturnSignal
吆视,就會(huì)拿到綁定信號(hào)的訂閱者典挑,把處理完成的信號(hào)內(nèi)容發(fā)送出來
2. 映射(flattenMap,Map)
-
flattenMap
,Map
用于把源信號(hào)內(nèi)容映射成新的內(nèi)容 - 在
Swift
中系統(tǒng)API就已經(jīng)有了這些函數(shù)的用法, 詳情可參考我的這篇文章Swift函數(shù)式編程之Map&Reduce&Filter
2-1. flattenMap
把源信號(hào)的內(nèi)容映射成一個(gè)新的信號(hào)啦吧,信號(hào)可以是任意類型
-
flattenMap
使用步驟:- 傳入一個(gè)
block
您觉,block
類型是返回值RACStream
,參數(shù)value
- 參數(shù)
value
就是源信號(hào)的內(nèi)容授滓,拿到源信號(hào)的內(nèi)容做處理 - 包裝成
RACReturnSignal
信號(hào)琳水,返回出去
- 傳入一個(gè)
-
flattenMap
底層實(shí)現(xiàn):- 0.
flattenMap
內(nèi)部調(diào)用bind
方法實(shí)現(xiàn)的,flattenMap
中block
的返回值,會(huì)作為bind
中bindBlock
的返回值般堆。 - 1.當(dāng)訂閱綁定信號(hào)在孝,就會(huì)生成
bindBlock
。 - 2.當(dāng)源信號(hào)發(fā)送內(nèi)容淮摔,就會(huì)調(diào)用
bindBlock(value, *stop)
- 3.調(diào)用
bindBlock
私沮,內(nèi)部就會(huì)調(diào)用flattenMap
的block
,flattenMap
的block
作用:就是把處理好的數(shù)據(jù)包裝成信號(hào) - 4.返回的信號(hào)最終會(huì)作為
bindBlock
中的返回信號(hào)和橙,當(dāng)做bindBlock
的返回信號(hào)仔燕。 - 5.訂閱
bindBlock
的返回信號(hào)造垛,就會(huì)拿到綁定信號(hào)的訂閱者,把處理完成的信號(hào)內(nèi)容發(fā)送出來
- 0.
- (__kindof RACStream *)flattenMap:(__kindof RACStream * (^)(id value))block {
Class class = self.class;
return [[self bind:^{
return ^(id value, BOOL *stop) {
id stream = block(value) ?: [class empty];
NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);
return stream;
};
}] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}
簡(jiǎn)單使用
@weakify(self)
[[_textField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
//源信號(hào)發(fā)出的時(shí)候涨享,就會(huì)調(diào)用這個(gè)block筋搏。
// 返回值:綁定信號(hào)的內(nèi)容.
return [RACReturnSignal return:[NSString stringWithFormat:@"flat輸出: %@", value]];
}] subscribeNext:^(id _Nullable x) {
@strongify(self)
//訂閱綁定信號(hào), 每當(dāng)原信號(hào)發(fā)送內(nèi)容, 處理后, 就會(huì)調(diào)用這個(gè)black
self.showLabel.text = x;
NSLog(@"%@", x);
}];
2-2. Map
Map作用:把源信號(hào)的值映射成一個(gè)新的值
-
Map
使用步驟:- 傳入一個(gè)
block
,類型是返回對(duì)象,參數(shù)是value
-
value
就是源信號(hào)的內(nèi)容厕隧,直接拿到源信號(hào)的內(nèi)容做處理 - 把處理好的內(nèi)容奔脐,直接返回就好了,不用包裝成信號(hào)吁讨,返回的值髓迎,就是映射的值。
- 傳入一個(gè)
-
Map
底層實(shí)現(xiàn):-
Map
底層其實(shí)是調(diào)用flatternMap
, Map中block
中的返回的值會(huì)作為flatternMap
中block中的值建丧。 - 當(dāng)訂閱綁定信號(hào)排龄,就會(huì)生成
bindBlock
。 - 當(dāng)源信號(hào)發(fā)送內(nèi)容翎朱,就會(huì)調(diào)用
bindBlock(value, *stop)
- 調(diào)用
bindBlock
橄维,內(nèi)部就會(huì)調(diào)用flattenMap
的block
-
flattenMap
的block
內(nèi)部會(huì)調(diào)用Map
中的block
,把Map中的block
返回的內(nèi)容包裝成返回的信號(hào)拴曲。 - 返回的信號(hào)最終會(huì)作為
bindBlock
中的返回信號(hào)争舞,當(dāng)做bindBlock
的返回信號(hào)。 - 訂閱
bindBlock
的返回信號(hào)澈灼,就會(huì)拿到綁定信號(hào)的訂閱者竞川,把處理完成的信號(hào)內(nèi)容發(fā)送出來
-
- (__kindof RACStream *)map:(id (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^(id value) {
return [class return:block(value)];
}] setNameWithFormat:@"[%@] -map:", self.name];
}
簡(jiǎn)單使用
//Map
[[_textField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
return [NSString stringWithFormat:@"map輸出: %@", value];
}] subscribeNext:^(id _Nullable x) {
@strongify(self)
self.showLabel.text = x;
NSLog(@"%@", x);
}];
//對(duì)數(shù)組的處理
NSArray *arr = @[@"2", @"3", @"a", @"g"];
RACSequence *sequence = [arr.rac_sequence map:^id _Nullable(id _Nullable value) {
return [NSString stringWithFormat:@"-%@-", value];
}];
NSLog(@"%@", [sequence array]);
/*輸出:
2018-03-24 14:13:32.421337+0800 ReactiveObjc[9043:492929] (
"-2-",
"-3-",
"-a-",
"-g-"
)
*/
2-3. FlatternMap
和 Map
的區(qū)別
-
FlatternMap
中的Block
返回信號(hào)。 -
Map
中的Block
返回對(duì)象叁熔。 - 開發(fā)中委乌,如果信號(hào)發(fā)出的值不是信號(hào),映射一般使用
Map
- 開發(fā)中荣回,如果信號(hào)發(fā)出的值是信號(hào)遭贸,映射一般使用
FlatternMap
- 信號(hào)中信號(hào)
- 當(dāng)一個(gè)信號(hào)需要返回另一個(gè)信號(hào)中的值的時(shí)候
- 讓我們來看看下面這個(gè)例子
#pragma 信號(hào)中信號(hào)
- (void)singleAndSingle {
//創(chuàng)建信號(hào)中信號(hào)
RACSubject *sonSingle = [RACSubject subject];
RACSubject *single = [RACSubject subject];
[[sonSingle flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
//sonSingle發(fā)送信號(hào)時(shí), 才會(huì)調(diào)用
return value;
}] subscribeNext:^(id _Nullable x) {
//只有sonSingle的子信號(hào), 大宋消息時(shí), 才會(huì)調(diào)用
NSLog(@"輸出: %@", x);
}];
//信號(hào)中信號(hào)發(fā)送子信號(hào)
[sonSingle sendNext:single];
//子信號(hào)發(fā)送內(nèi)容
[single sendNext:@123];
}
3. 組合
3-1. concat
按照某一固定順序拼接信號(hào),當(dāng)多個(gè)信號(hào)發(fā)出的時(shí)候心软,有順序的接收信號(hào)
//讓我們先看一下一般的正常操作
- (void)setConcatAction {
//當(dāng)需要按順序執(zhí)行的時(shí)候: 先執(zhí)行A, 在執(zhí)行B
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACReplaySubject subject];
NSMutableArray *array = [NSMutableArray array];
//訂閱信號(hào)
[subjectA subscribeNext:^(id _Nullable x) {
[array addObject:x];
}];
[subjectB subscribeNext:^(id _Nullable x) {
[array addObject:x];
}];
//發(fā)送信號(hào)
[subjectB sendNext:@"B"];
[subjectA sendNext:@"A"];
[subjectA sendCompleted];
//輸出: [B, A]
NSLog(@"%@", array);
}
- 很明顯, 上述的結(jié)果并未達(dá)到我們的需求: 限制性A, 在執(zhí)行B
- 下面我們看看使用
concat
后的執(zhí)行情況
- (void)setConcatAction {
//當(dāng)需要按順序執(zhí)行的時(shí)候: 先執(zhí)行A, 在執(zhí)行B
RACSubject *subC = [RACSubject subject];
RACSubject *subD = [RACReplaySubject subject];
NSMutableArray *array2 = [NSMutableArray array];
//訂閱信號(hào)
[[subC concat:subD] subscribeNext:^(id _Nullable x) {
[array2 addObject:x];
}];
//發(fā)送信號(hào)
[subD sendNext:@"D"];
[subC sendNext:@"C"];
[subC sendCompleted];
//輸出: [C, D]
NSLog(@"%@", array2);
}
- 可以看到, 輸出的結(jié)果和我們預(yù)想的一樣, 順序輸出
- 那么,
concat
的底層到底是如何實(shí)現(xiàn)的呢?
- (RACSignal *)concat:(RACSignal *)signal {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACCompoundDisposable *compoundDisposable = [[RACCompoundDisposable alloc] init];
RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
RACDisposable *concattedDisposable = [signal subscribe:subscriber];
[compoundDisposable addDisposable:concattedDisposable];
}];
[compoundDisposable addDisposable:sourceDisposable];
return compoundDisposable;
}] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
}
-
concat
底層實(shí)現(xiàn):- 當(dāng)拼接信號(hào)被訂閱革砸,就會(huì)調(diào)用拼接信號(hào)的
didSubscribe
-
didSubscribe
中,會(huì)先訂閱第一個(gè)源信號(hào)subjectA
- 會(huì)執(zhí)行第一個(gè)源信號(hào)
subjectA
的didSubscribe
- 第一個(gè)源信號(hào)
subjectA
的didSubscribe
中發(fā)送值糯累,就會(huì)調(diào)用第一個(gè)源信號(hào)subjectA
訂閱者的nextBlock
, 通過拼接信號(hào)的訂閱者把值發(fā)送出來. - 第一個(gè)源信號(hào)
subjectA
的didSubscribe
中發(fā)送完成,就會(huì)調(diào)用第一個(gè)源信號(hào)subjectA
訂閱者的completedBlock
,訂閱第二個(gè)源信號(hào)subjectB
這時(shí)候才激活subjectB
- 訂閱第二個(gè)源信號(hào)
subjectB
,執(zhí)行第二個(gè)源信subjectB
號(hào)的didSubscribe
- 第二個(gè)源信號(hào)
subjectB
的didSubscribe
中發(fā)送值,就會(huì)通過拼接信號(hào)的訂閱者把值發(fā)送出來.
- 當(dāng)拼接信號(hào)被訂閱革砸,就會(huì)調(diào)用拼接信號(hào)的
3-2. then
用于連接兩個(gè)信號(hào)册踩,當(dāng)?shù)谝粋€(gè)信號(hào)完成泳姐,才會(huì)連接then返回的信號(hào)
- (RACSignal *)then:(RACSignal * (^)(void))block {
NSCParameterAssert(block != nil);
return [[[self
ignoreValues]
concat:[RACSignal defer:block]]
setNameWithFormat:@"[%@] -then:", self.name];
}
//ignoreValues底層實(shí)現(xiàn)
- (RACSignal *)ignoreValues {
return [[self filter:^(id _) {
return NO;
}] setNameWithFormat:@"[%@] -ignoreValues", self.name];
}
- 實(shí)現(xiàn)原理
- 底層會(huì)調(diào)用
filter
過濾掉本身信號(hào)發(fā)出的值(filter
后面會(huì)講到) - 然后再使用
concat
連接then
返回的信號(hào) - 下面是測(cè)試用例
- 底層會(huì)調(diào)用
- (void)setThenAction {
RACSubject *subjectA = [RACReplaySubject subject];
RACSubject *subjectB = [RACReplaySubject subject];
//發(fā)送信號(hào)
[subjectA sendNext:@"A"];
[subjectA sendCompleted];
[subjectB sendNext:@"B"];
//訂閱信號(hào)
[[subjectA then:^RACSignal * _Nonnull{
return subjectB;
}] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
//這里只會(huì)輸出: B
//不會(huì)輸出: A
}
3-3. merge
把多個(gè)信號(hào)合并為一個(gè)信號(hào),任何一個(gè)信號(hào)有新值的時(shí)候就會(huì)調(diào)用
- (RACSignal *)merge:(RACSignal *)signal {
return [[RACSignal
merge:@[ self, signal ]]
setNameWithFormat:@"[%@] -merge: %@", self.name, signal];
}
+ (RACSignal *)merge:(id<NSFastEnumeration>)signals {
NSMutableArray *copiedSignals = [[NSMutableArray alloc] init];
for (RACSignal *signal in signals) {
[copiedSignals addObject:signal];
}
return [[[RACSignal
createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
for (RACSignal *signal in copiedSignals) {
[subscriber sendNext:signal];
}
[subscriber sendCompleted];
return nil;
}]
flatten]
setNameWithFormat:@"+merge: %@", copiedSignals];
}
- 底層實(shí)現(xiàn)
- 1.合并信號(hào)被訂閱的時(shí)候暂吉,就會(huì)遍歷所有信號(hào)胖秒,并且發(fā)出這些信號(hào)缎患。
- 2.每發(fā)出一個(gè)信號(hào),這個(gè)信號(hào)就會(huì)被訂閱
- 3.也就是合并信號(hào)一被訂閱阎肝,就會(huì)訂閱里面所有的信號(hào)挤渔。
- 4.只要有一個(gè)信號(hào)被發(fā)出就會(huì)被監(jiān)聽。
- (void)setMergeAction {
// 只要想無序的整合信號(hào)數(shù)據(jù)
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACSubject subject];
RACSubject *subjectC = [RACSubject subject];
//合并信號(hào)
RACSignal *single = [[subjectA merge:subjectB] merge:subjectC];
//訂閱信號(hào)
[single subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
//發(fā)出消息
[subjectA sendNext:@"A"];
[subjectC sendNext:@"C"];
[subjectB sendNext:@"B"];
}
//輸出結(jié)果(分別輸出): A, C, B
3-4. zipWith
把兩個(gè)信號(hào)壓縮成一個(gè)信號(hào)风题,只有當(dāng)兩個(gè)信號(hào)同時(shí)發(fā)出信號(hào)內(nèi)容時(shí)判导,并且把兩個(gè)信號(hào)的內(nèi)容合并成一個(gè)元組,才會(huì)觸發(fā)壓縮流的next事件
- 底層實(shí)現(xiàn):
- 1.定義壓縮信號(hào)沛硅,內(nèi)部就會(huì)自動(dòng)訂閱
subjectA
眼刃,subjectB
- 2.每當(dāng)
subjectA
或者subjectB
發(fā)出信號(hào),就會(huì)判斷subjectA
摇肌,subjectB
有沒有發(fā)出個(gè)信號(hào)擂红,有就會(huì)把最近發(fā)出的信號(hào)都包裝成元組發(fā)出。
- 1.定義壓縮信號(hào)沛硅,內(nèi)部就會(huì)自動(dòng)訂閱
- (void)setZipwithAction {
// 只要想無序的整合信號(hào)數(shù)據(jù)
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACSubject subject];
//合并信號(hào)
RACSignal *single = [subjectA zipWith:subjectB];
//訂閱信號(hào)
[single subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
//發(fā)出消息
[subjectA sendNext:@"A"];
[subjectB sendNext:@"B"];
/* 輸出:
(A, B)
*/
}
3-5. combineLatest
- 將多個(gè)信號(hào)合并起來围小,并且拿到各個(gè)信號(hào)的最新的值
- 必須每個(gè)合并的信號(hào)至少都有過一次
sendNext
昵骤,才會(huì)觸發(fā)合并的信號(hào) - 這里我們考慮這樣一個(gè)需求: 在登錄頁(yè)面, 只有在賬號(hào)密碼都輸入的情況下, 登錄按鈕才可點(diǎn)擊, 否則不可點(diǎn)擊
- 正常情況下我們需要監(jiān)聽每一個(gè)文本框的輸入
- 下面我們來看一下
combineLatest
控制登錄按鈕是否可點(diǎn)擊
- (void)setCombineLatest {
//把兩個(gè)信號(hào)組合成一個(gè)信號(hào),跟zip一樣,沒什么區(qū)別
RACSignal *single = [_accountText.rac_textSignal combineLatestWith:_passwordText.rac_textSignal];
[single subscribeNext:^(id _Nullable x) {
RACTupleUnpack(NSString *account, NSString *password) = x;
_loginButton.enabled = account.length > 0 && password.length > 0;
}];
}
- 底層實(shí)現(xiàn):
- 1.當(dāng)組合信號(hào)被訂閱肯适,內(nèi)部會(huì)自動(dòng)訂閱兩個(gè)信號(hào),必須兩個(gè)信號(hào)都發(fā)出內(nèi)容变秦,才會(huì)被觸發(fā)。(而zip, 是兩個(gè)信號(hào)同事發(fā)出內(nèi)容, 才會(huì)觸發(fā))
- 2.把兩個(gè)信號(hào)組合成元組發(fā)出疹娶。
3-6. reduce
聚合:用于信號(hào)發(fā)出是元組的內(nèi)容伴栓,把信號(hào)發(fā)出元組的值聚合成一個(gè)值
這里我們把上面的代碼, 使用
RACSingle
的一個(gè)類方法優(yōu)化一下
- (void)setReduceAction {
// reduce:把多個(gè)信號(hào)的值,聚合為一個(gè)值
RACSignal *single = [RACSignal combineLatest:@[_accountText.rac_textSignal, _passwordText.rac_textSignal] reduce:^id (NSString *account, NSString *password){
return @(account.length > 0 && password.length > 0);
}];
[single subscribeNext:^(id _Nullable x) {
_loginButton.enabled = [x boolValue];
}];
}
-
RACSingle
類方法- 參數(shù)一:
(id<NSFastEnumeration>)
類型-
NSFastEnumeration
我們?cè)谏弦黄恼?a target="_blank" rel="nofollow">ReactiveCocoa之集合使用詳解02中簡(jiǎn)單介紹過 -
NSFastEnumeration
: 是一個(gè)協(xié)議, 所有遵循該協(xié)議的類, 均可視為一個(gè)數(shù)組, 例如NSArray
- 故這里, 應(yīng)該傳一個(gè)包含
RACSingle
信號(hào)的數(shù)組
-
- 參數(shù)二:
(RACGenericReduceBlock)reduceBlock
是一個(gè)black
- 參數(shù)一:
typedef ValueType _Nonnull (^RACGenericReduceBlock)();
//reduceblcok中的參數(shù),有多少信號(hào)組合雨饺,reduceblcok就有多少參數(shù)钳垮,每個(gè)參數(shù)就是之前信號(hào)發(fā)出的內(nèi)容
這里用一個(gè)宏, 急需將上面的代碼簡(jiǎn)化一下
- (void)setReduceAction {
RAC(_loginButton, enabled) = [RACSignal combineLatest:@[_accountText.rac_textSignal, _passwordText.rac_textSignal] reduce:^id (NSString *account, NSString *password){
return @(account.length > 0 && password.length > 0);
}];
}
上面用到了一個(gè)宏RAC
, 這里暫不贅述, 以后會(huì)集中整理一下 RAC中的宏, 具體實(shí)現(xiàn)如下
#define RAC(TARGET, ...) \
metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
(RAC_(TARGET, __VA_ARGS__, nil)) \
(RAC_(TARGET, __VA_ARGS__))
/// Do not use this directly. Use the RAC macro above.
#define RAC_(TARGET, KEYPATH, NILVALUE) \
[[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(TARGET) nilValue:(NILVALUE)][@keypath(TARGET, KEYPATH)]
4. 過濾
4-1. filter
過濾信號(hào), 過濾掉不符合條件的信號(hào)
- (void) filterAction{
//filter
//截取等于11位的字符
[[_accountText.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
//類似手機(jī)號(hào)的輸入, 只有等于11位的時(shí)候才返回true
return value.length == 11;
}]subscribeNext:^(NSString * _Nullable x) {
//這里只會(huì)返回等于11位的字符
NSLog(@"filter = %@", x);
}];
}
filter
底層是調(diào)用的flatMap
方法, 如下:
- (__kindof RACStream *)filter:(BOOL (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^ id (id value) {
if (block(value)) {
return [class return:value];
} else {
return class.empty;
}
}] setNameWithFormat:@"[%@] -filter:", self.name];
}
4-2. ignore
忽略掉某些特定值的信號(hào)
- (void)setIgnoreAction {
///ignore
//這里的測(cè)試只有第一個(gè)字符位: m的時(shí)候能看到效果
[[_accountText.rac_textSignal ignore:@"m"] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"ignore = %@", x);
}];
//ignoreValues: 忽略所有信號(hào)
[[_passwordText.rac_textSignal ignoreValues] subscribeNext:^(id _Nullable x) {
NSLog(@"allIgnore = %@", x);
}];
}
ignore
方法的底層都是調(diào)用的filter
方法
//ignore
- (__kindof RACStream *)ignore:(id)value {
return [[self filter:^ BOOL (id innerValue) {
return innerValue != value && ![innerValue isEqual:value];
}] setNameWithFormat:@"[%@] -ignore: %@", self.name, RACDescription(value)];
}
//ignoreValues
- (RACSignal *)ignoreValues {
return [[self filter:^(id _) {
return NO;
}] setNameWithFormat:@"[%@] -ignoreValues", self.name];
}
4-3. distinctUntilChanged
- 當(dāng)上一次的值和當(dāng)前的值有明顯的變化就會(huì)發(fā)出信號(hào),否則會(huì)被忽略掉额港。
- 在開發(fā)中饺窿,刷新UI經(jīng)常使用,只有兩次數(shù)據(jù)不一樣才需要刷新
//distinctUntilChanged
- (void)setdistinctUntilChanged {
//創(chuàng)建信號(hào)
RACSubject *subject = [RACSubject subject];
//訂閱
[[subject distinctUntilChanged] subscribeNext:^(id _Nullable x) {
NSLog(@"distinctUntilChanged = %@", x);
}];
[subject sendNext:@12];
[subject sendNext:@12];
[subject sendNext:@23];
/*輸出結(jié)果:只會(huì)輸出兩次
distinctUntilChanged = 12
distinctUntilChanged = 23
*/
}
distinctUntilChanged
底層是調(diào)用的bind
高級(jí)用法
- (__kindof RACStream *)distinctUntilChanged {
Class class = self.class;
return [[self bind:^{
__block id lastValue = nil;
__block BOOL initial = YES;
return ^(id x, BOOL *stop) {
if (!initial && (lastValue == x || [x isEqual:lastValue])) return [class empty];
initial = NO;
lastValue = x;
return [class return:x];
};
}] setNameWithFormat:@"[%@] -distinctUntilChanged", self.name];
}
4-4. take
從開始一共取N次的信號(hào), 當(dāng)遇到sendCompleted
語句執(zhí)行時(shí), 會(huì)提前停止發(fā)送信號(hào)
- (void)setTakeAndTakeLast {
//take
RACSubject *subject1 = [RACSubject subject];
[[subject1 take:2] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
[subject1 sendNext:@1];
[subject1 sendNext:@2];
[subject1 sendCompleted];
[subject1 sendNext:@3];
//分別輸出: 1, 2
}
//如果上面發(fā)送信號(hào)的代碼調(diào)整為
[subject1 sendNext:@1];
[subject1 sendCompleted];
[subject1 sendNext:@2];
[subject1 sendNext:@3];
//那么輸出結(jié)果將會(huì),只輸出: 1
4-5. takeLast
取調(diào)用sendCompleted
之前的N次信號(hào),前提條件移斩,訂閱者必須調(diào)用sendCompleted
肚医,否則不會(huì)執(zhí)行任何操作
- (void)setTakeAndTakeLast {
//takeLast
RACSubject *subject1 = [RACSubject subject];
[[subject1 takeLast:2] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
[subject1 sendNext:@1];
[subject1 sendNext:@2];
[subject1 sendNext:@3];
[subject1 sendCompleted];
}
4-6. takeUntil
只要傳入的信號(hào)發(fā)送完成或者subject2
開始發(fā)送信號(hào)的時(shí)候,就不會(huì)再接收信號(hào)的內(nèi)容
- (void)setTakeAndTakeLast {
//takeUntil
RACSubject *subject1 = [RACSubject subject];
RACSubject *subject2 = [RACSubject subject];
[[subject1 takeUntil:subject2] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
[subject1 sendNext:@11];
[subject1 sendNext:@12];
// [subject1 sendCompleted];
[subject1 sendNext:@13];
[subject2 sendNext:@"21"];
[subject2 sendNext:@"22"];
//這樣會(huì)輸出: 11, 12, 13
//當(dāng)sendCompleted取消注釋的時(shí)候, 只會(huì)輸出: 11, 12
}
4-7. switchToLatest
- 主要用于信號(hào)的信號(hào), 有時(shí)候也會(huì)發(fā)出信號(hào), 會(huì)在信號(hào)的信號(hào)中獲取其發(fā)送的最新的信號(hào)
- 方法的底層是調(diào)用了
flattenMap
方法
- (RACSignal *)switchToLatest {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACMulticastConnection *connection = [self publish];
RACDisposable *subscriptionDisposable = [[connection.signal
flattenMap:^(RACSignal *x) {
NSCAssert(x == nil || [x isKindOfClass:RACSignal.class], @"-switchToLatest requires that the source signal (%@) send signals. Instead we got: %@", self, x);
// -concat:[RACSignal never] prevents completion of the receiver from
// prematurely terminating the inner signal.
return [x takeUntil:[connection.signal concat:[RACSignal never]]];
}]
subscribe:subscriber];
RACDisposable *connectionDisposable = [connection connect];
return [RACDisposable disposableWithBlock:^{
[subscriptionDisposable dispose];
[connectionDisposable dispose];
}];
}] setNameWithFormat:@"[%@] -switchToLatest", self.name];
}
下面我們看一下具體的使用示例
- (void)setswitchToLatest {
//信號(hào)的信號(hào)
RACSubject *subject1 = [RACSubject subject];
RACSubject *subject2 = [RACSubject subject];
//獲取信號(hào)中信號(hào)最近發(fā)出信號(hào),訂閱最近發(fā)出的信號(hào)
[[subject1 switchToLatest] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
//發(fā)送信號(hào)
[subject1 sendNext:subject2];
[subject2 sendNext:@"信號(hào)中信號(hào)"];
//最終結(jié)果輸出: "信號(hào)中信號(hào)"
}
4-8. skip
跳過N個(gè)信號(hào)后, 再開始訂閱信號(hào)
- (void)setSkipAction {
//創(chuàng)建信號(hào)
RACSubject *subject = [RACSubject subject];
//訂閱信號(hào)
//要求跳過2個(gè)信號(hào)
[[subject skip:2] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
//發(fā)送信號(hào)
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
[subject sendNext:@4];
//因?yàn)樯厦嫣^了兩個(gè)信號(hào), 所以這里只會(huì)輸出: 3, 4
}
5. 定時(shí)操作
5-1. interval
定時(shí)器, 每隔一段時(shí)間發(fā)出信號(hào)
//RAC定時(shí)器, 每隔一段時(shí)間執(zhí)行一次
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"定時(shí)器");
}];
其中RACScheduler
是RAC
中管理線程的類
5-2. delay
延遲一段時(shí)間都發(fā)送信號(hào)
//delay: 延遲執(zhí)行
[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"delay"];
return nil;
}] delay:2] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
5-3. timeout
超時(shí), 可以讓一個(gè)信號(hào)在一定時(shí)間后自動(dòng)報(bào)錯(cuò)
//timeout: 超時(shí), 可以讓一個(gè)信號(hào)在一定時(shí)間后自動(dòng)報(bào)錯(cuò)
RACSignal *single = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
return nil;
}] timeout:2 onScheduler:[RACScheduler currentScheduler]];
[single subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
} error:^(NSError * _Nullable error) {
//2秒后自動(dòng)調(diào)用
NSLog(@"%@", error);
}];
6. 重復(fù)操作
6-1. retry
重試 :只要失敗向瓷,就會(huì)重新執(zhí)行創(chuàng)建信號(hào)中的block,直到成功.
- (void)setResertAction {
//retry
__block int i = 0;
[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
if (i == 5) {
[subscriber sendNext:@12];
} else {
NSLog(@"發(fā)生錯(cuò)誤");
[subscriber sendError:nil];
}
i++;
return nil;
}] retry] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
} error:^(NSError * _Nullable error) {
NSLog(@"%@", error);
}];
/*輸出結(jié)果
2018-03-30 15:44:08.412860+0800 ReactiveObjc[4125:341376] 發(fā)生錯(cuò)誤
2018-03-30 15:44:08.461105+0800 ReactiveObjc[4125:341376] 發(fā)生錯(cuò)誤
2018-03-30 15:44:08.461897+0800 ReactiveObjc[4125:341376] 發(fā)生錯(cuò)誤
2018-03-30 15:44:08.462478+0800 ReactiveObjc[4125:341376] 發(fā)生錯(cuò)誤
2018-03-30 15:44:08.462913+0800 ReactiveObjc[4125:341376] 發(fā)生錯(cuò)誤
2018-03-30 15:44:08.463351+0800 ReactiveObjc[4125:341376] 12
*/
}
6-2. replay
重放:當(dāng)一個(gè)信號(hào)被多次訂閱,反復(fù)播放內(nèi)容
//replay
RACSignal *single = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@23];
[subscriber sendNext:@34];
return nil;
}] replay];
[single subscribeNext:^(id _Nullable x) {
NSLog(@"第一次訂閱-%@", x);
}];
[single subscribeNext:^(id _Nullable x) {
NSLog(@"第二次訂閱-%@", x);
}];
/*輸出結(jié)果:
2018-03-30 15:51:20.115052+0800 ReactiveObjc[4269:361568] 第一次訂閱-23
2018-03-30 15:51:20.115195+0800 ReactiveObjc[4269:361568] 第一次訂閱-34
2018-03-30 15:51:20.115278+0800 ReactiveObjc[4269:361568] 第二次訂閱-23
2018-03-30 15:51:20.115352+0800 ReactiveObjc[4269:361568] 第二次訂閱-34
*/
6-3. throttle
節(jié)流:當(dāng)某個(gè)信號(hào)發(fā)送比較頻繁時(shí)肠套,可以使用節(jié)流, 在一定時(shí)間(1秒)內(nèi),不接收任何信號(hào)內(nèi)容猖任,過了這個(gè)時(shí)間(1秒)獲取最后發(fā)送的信號(hào)內(nèi)容發(fā)出你稚。
RACSubject *subject = [RACSubject subject];
[[subject throttle:0.001] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
[subject sendNext:@10];
[subject sendNext:@11];
[subject sendNext:@12];
[subject sendNext:@13];
[subject sendNext:@14];
[subject sendNext:@15];
[subject sendNext:@16];
[subject sendNext:@17];
[subject sendNext:@18];
//這里因?yàn)閳?zhí)行的速度非常快, 所以這里輸出的結(jié)果只有最后一個(gè): 18
- 以上就是RAC中的一些常用的高級(jí)用用法具體講解和使用示例
- 如有不足之處, 還請(qǐng)多多指教, 后期會(huì)持續(xù)更新相關(guān)知識(shí)點(diǎn)
- 下面是RAC相關(guān)的兩篇文章