信號類還提供了一列方法,可以對信號傳遞的數據進行操作。
1.map:方法
map:方法可以將信號傳遞的數據進行映射和轉換,demo如下:
RACSignal *signal = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"test"]; //傳遞"test"
}];
RACSignal *mappedSignal = [signal map:^id(NSString *value) {
return @(value.length); //映射成"test"字符串的長度
}];
[mappedSignal subscribeNext:^(id x) {
NSInteger len = [(NSNumber *)x integerValue];
NSLog(@"%@",@(len)); //輸出結果,結果是4
} completed:^{
NSLog(@"completed");
}];
上一篇已經分析了create:方法,創建了一個RACDynamicSignal類型的信號signal,當執行訂閱subscriber時會觸發didSubscribe回調,調用[subscriber sendNext:@"test"]。本例不是由signal直接訂閱subscriber,而是map方法后新的mappedSignal對象訂閱。map:方法如下:
- (RACSignal *)map:(id (^)(id value))block {
return [super map:block]; //調用父類(RACStrem)的map:方法
}
- (instancetype)map:(id (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
//調用flattenMap:方法
return [[self flattenMap:^(id value) {
return [class return:block(value)];
}] setNameWithFormat:@"[%@] -map:", self.name];
}
首先調用父類RACStrem的map:方法,接著調用flattenMap:方法,該方法默認由RACStrem類實現,如下:
- (instancetype)flattenMap:(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];
}
實質是通過bind方法創建一個新的信號。但是由于子類RACSignal重現了flattenMap:方法,故不會調用bind:方法,而是調用如下方法:
- (RACSignal *)flattenMap:(RACSignal * (^)(id value))signalBlock {
//創建一個RACDynamicSignal,訂閱subscriber時會觸發didSubscribe回調
return [[RACSignal
create:^(id<RACSubscriber> subscriber) {
__block volatile int32_t subscriptions = 0;
//1.定義一個回調subscribeSignal,傳入signal和nextBlock作為參數
void (^subscribeSignal)(RACSignal *, void (^)(id)) = ^(RACSignal *signal, void (^nextBlock)(id)) {
__block RACDisposable *savedDisposable;
//訂閱信號
[signal subscribeSavingDisposable:^(RACDisposable *disposable) {
savedDisposable = disposable;
OSAtomicIncrement32(&subscriptions);
[subscriber.disposable addDisposable:savedDisposable];
} next:nextBlock error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber.disposable removeDisposable:savedDisposable];
if (OSAtomicDecrement32(&subscriptions) == 0) [subscriber sendCompleted];
}];
};
//2.觸發subscribeSignal回調,傳入信號和nextBlock
subscribeSignal(self, ^(id x) {
//創建一個新的signal
RACSignal *innerSignal = signalBlock(x);
if (innerSignal == nil) return;
NSCAssert([innerSignal isKindOfClass:RACSignal.class], @"Expected a RACSignal, got %@", innerSignal);
//3.再次調用subscribeSignal回調,傳入新的innerSignal和nextBlock
subscribeSignal(innerSignal, ^(id x) {
[subscriber sendNext:x]; //4.初始訂閱者的next事件
});
});
}]
setNameWithFormat:@"[%@] -flattenMap:", self.name];
}
這是一個比較長的方法,根據注釋,我們將它分成4個部分分析。
1.首先我們將demo中調用flattenMap:方法的信號對象記為signal,調用create:方法返回一個新的RACDynamicSignal,記為mappedSignal。
2.mappedSignal在demo中調用subscribeNext:completed:方法時,創建一個subscriber,記為subscriber0,同時觸發mappedSignal的didSubscribe回調,進入步驟1。
3.首先定義一個subscribeSignal方法,傳入兩個參數,分別是要訂閱的信號和訂閱時執行的nextBlock,然后進行signal的訂閱。
4.調用subscribeSignal方法,傳入signal,觸發signal的didSubscribe邏輯,在demo中是傳遞觸發next事件,傳入"test",如下:
RACSignal *signal = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"test"]; //傳遞"test"
}];
5.傳回的參數x為"test",然后執行signalBlock(x),signalBlock由上一級map:方法傳入,返回一個RACReturnSignal對象,內部value值是外層轉化后的值,即字符串"test"的長度4,如下:
- (instancetype)map:(id (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^(id value) {
return [class return:block(value)]; //返回一個新的RACReturnSignal對象,value是block轉化后的值,即為4
}] setNameWithFormat:@"[%@] -map:", self.name];
}
6.RACReturnSignal對象就是innerSignal,然后再次調用subscribeSignal方法,傳入innerSignal和相應的nextBlock。前一篇文章分析過,對RACReturnSignal類型的對象innerSignal訂閱,attachSubscriber:方法會直接觸發nextBlock,傳遞value值,即4。最后在nextBlock中觸發初始訂閱者subscriber0的next事件,傳遞4,并輸出。
2.filter:方法
filter:方法可以過濾信號的值,來決定后續數據是否繼續傳遞,demo如下:
RACSignal *signal = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"test"];
}];
RACSignal *filteredSignal = [signal filter:^BOOL(NSString *value) {
if (value.length>=4) { //字符串字數大于等于4,信號繼續傳遞
return YES;
} else { //字符串字數小于4,信號終止傳遞
return NO;
}
}];
[filteredSignal subscribeNext:^(id x) {
NSString *str = (NSString *)x;
NSLog(str); //輸出"test"
} completed:^{
NSLog(@"completed");
}];
在filter:方法里面通過返回YES或者NO來決定信號數據是否繼續傳遞,demo中根據字符串的個數來判斷。如果返回YES,則輸出字符串,否則不輸出。filter:方法如下:
- (RACSignal *)filter:(BOOL (^)(id value))block {
return [super filter:block]; //調用基類RACStream的filter:方法
}
- (instancetype)filter:(BOOL (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
//RACSignal的flatternMap:方法
return [[self flattenMap:^ id (id value) {
if (block(value)) {
return [class return:value];
} else {
return class.empty;
}
}] setNameWithFormat:@"[%@] -filter:", self.name];
}
調用基類RACStream的filter:方法,和map:方法類似,最終也會調用flattenMap:方法,不同的是,filter:方法傳遞的block邏輯不一樣,如果外層block返回YES,則返回一個RACReturnSignal,內部value仍然是原值,如果外層返回NO,則返回一個RACEmptySignal。
回顧一下flattenMap:方法內部處理邏輯,如下:
- (RACSignal *)flattenMap:(RACSignal * (^)(id value))signalBlock {
return [[RACSignal
create:^(id<RACSubscriber> subscriber) {
__block volatile int32_t subscriptions = 0;
void (^subscribeSignal)(RACSignal *, void (^)(id)) = ^(RACSignal *signal, void (^nextBlock)(id)) {
__block RACDisposable *savedDisposable;
[signal subscribeSavingDisposable:^(RACDisposable *disposable) {
savedDisposable = disposable;
OSAtomicIncrement32(&subscriptions);
[subscriber.disposable addDisposable:savedDisposable];
} next:nextBlock error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
//RACReturnSignal和RACEmptySignal在attachSubscriber都會觸發completed事件
[subscriber.disposable removeDisposable:savedDisposable];
if (OSAtomicDecrement32(&subscriptions) == 0) {
[subscriber sendCompleted];
}
}];
};
subscribeSignal(self, ^(id x) {
RACSignal *innerSignal = signalBlock(x);
if (innerSignal == nil) return;
NSCAssert([innerSignal isKindOfClass:RACSignal.class], @"Expected a RACSignal, got %@", innerSignal);
subscribeSignal(innerSignal, ^(id x) {
[subscriber sendNext:x];
});
});
}]
setNameWithFormat:@"[%@] -flattenMap:", self.name];
}
如果innerSignal是RACReturnSignal對象,則再次調用subscribeSignal方法,邏輯和map:方法一樣。如果innerSignal是RACEmptySignal對象,則再次調用subscribeSignal方法,信號訂閱時,會觸發RACEmptySignal的attachSubscriber:方法,如下:
- (void)attachSubscriber:(RACLiveSubscriber *)subscriber {
NSCParameterAssert(subscriber != nil);
[subscriber sendCompleted]; //completed事件
}
只會觸發completed事件,不會觸發subscriber的next事件,也就不會觸發初始訂閱者subscriber0的next事件,不會輸出。從而實現filter過濾信號數據的作用。
ignore:方法是在filter:方法的基礎上封裝了一下,實現指定信號數據的忽略。
- (instancetype)ignore:(id)value {
return [[self filter:^ BOOL (id innerValue) {
return innerValue != value && ![innerValue isEqual:value];
}] setNameWithFormat:@"[%@] -ignore: %@", self.name, [value rac_description]];
}
3.concat:方法
concat:方法用于將若干個signal按順序連接,signal按照先后順序依次訂閱subscriber。demo如下:
RACSignal *signal1 = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"one"];//發送next事件
[subscriber sendCompleted];//發送completed事件
}];
RACSignal *signal2 = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"two"];//發送next事件
[subscriber sendCompleted];//發送completed事件
}];
[[signal1 concat:signal2] subscribeNext:^(id x) {
NSString *str = (NSString *)x;
NSLog(str); //輸出字符串
} completed:^{
NSLog(@"completed");//輸出"completed"
}];
執行demo,輸出的順序是"one","two","completed"。分析concat:方法,如下:
- (RACSignal *)concat:(RACSignal *)signal {
return [[RACSignal create:^(id<RACSubscriber> subscriber) {
[self subscribeSavingDisposable:^(RACDisposable *disposable) {
[subscriber.disposable addDisposable:disposable];
} next:^(id x) {
[subscriber sendNext:x]; //觸發next事件
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[signal subscribe:subscriber]; //在completed事件回調中觸發下一個signal的訂閱邏輯
}];
}] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
}
在demo中,首先signal1被訂閱,sendNext:方法觸發next事件,將數據傳遞給subscriber,輸出"one"。然后sendCompleted:方法,觸發completed事件,引起下一個信號對象signal2的訂閱邏輯。如果signal1不調用sendCompleted方法,則結束流程。subscriber訂閱signal2后,sendNext:方法觸發next事件,輸出字符串"two",sendCompleted方法觸發completed事件,輸出"completed"。
4.merge:方法
merge:方法將多個信號連接起來,實現多個信號依次被訂閱。demo如下:
RACSignal *signal1 = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"test1"];
}];
RACSignal *signal2 = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"test2"];
}];
RACSignal *mergeSignal = [signal1 merge:signal2];
[mergeSignal subscribeNext:^(id x) {
NSLog(@"%@",x); //依次輸出"test1"、"test2"
}];
mergerSignal訂閱subscriber時,依次執行signal1和signal2的didSubscribe回調,輸出"test1","test2"。
merge:方法實現如下:
- (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 [[copiedSignals.rac_signal flatten] setNameWithFormat:@"+merge: %@", copiedSignals];
}
首先將signal1和signal2存入一個數組中,然后執行NSArray的rac_signal方法,創建一個RACDynamicSignal,訂閱時,依次觸發next事件,并且將signal1和signal2作為參數傳遞。
- (RACSignal *)rac_signal {
NSArray *collection = [self copy];
return [[RACSignal create:^(id<RACSubscriber> subscriber) {
for (id obj in collection) {
[subscriber sendNext:obj];
if (subscriber.disposable.disposed) return;
}
[subscriber sendCompleted];
}] setNameWithFormat:@"%@ -rac_signal", self.rac_description];
}
flatten:方法調用flattenMap:方法,根據上文對flattenMap:方法的分析,首先依次觸發next事件,傳遞signal1和signal2,然后調用signalBlock,返回value。然后調用subscribeSignal()方法,觸發next事件,依次輸出test1和test2。
- (instancetype)flatten {
__weak RACStream *stream __attribute__((unused)) = self;
return [[self flattenMap:^(id value) {
return value;
}] setNameWithFormat:@"[%@] -flatten", self.name];
}
5.zipWith:方法
zipWith:方法的作用是將兩個信號發出的數據壓縮成一個傳遞,即subscriber調用一次sendNext,demo如下:
RACSignal *signal1 = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"test1"];
[subscriber sendCompleted];
}];
RACSignal *signal2 = [RACSignal create:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"test2"];
[subscriber sendCompleted];
}];
RACSignal *zipSignal = [signal1 zipWith:signal2];
[zipSignal subscribeNext:^(id x) {
RACTuple *tuple = (RACTuple *)x;
for (NSString *value in tuple.array) {
NSLog(@"%@",value);
}
} completed:^{
NSLog(@"completed");
}];
signal1和signal2被zipWith:方法壓縮成一個信號zipSignal,輸出的內容是一個RACTuple對象,包含signal1和signal2發送的數據"test1"和"test2",依次輸出。下面分析一下zipWith:方法:
- (RACSignal *)zipWith:(RACSignal *)signal {
NSCParameterAssert(signal != nil);
//創建一個RACDynamic信號
return [[RACSignal create:^(id<RACSubscriber> subscriber) {
//管理signal1的數據集合
__block BOOL selfCompleted = NO;
NSMutableArray *selfValues = [NSMutableArray array];
//管理signal2的數據集合
__block BOOL otherCompleted = NO;
NSMutableArray *otherValues = [NSMutableArray array];
//是否觸發subscriber的completed事件
void (^sendCompletedIfNecessary)(void) = ^{
@synchronized (selfValues) {
BOOL selfEmpty = (selfCompleted && selfValues.count == 0);
BOOL otherEmpty = (otherCompleted && otherValues.count == 0);
if (selfEmpty || otherEmpty) [subscriber sendCompleted];
}
};
//是否觸發subscriber的next事件
void (^sendNext)(void) = ^{
@synchronized (selfValues) {
if (selfValues.count == 0) return;
if (otherValues.count == 0) return;
RACTuple *tuple = [RACTuple tupleWithObjects:selfValues[0], otherValues[0], nil];
[selfValues removeObjectAtIndex:0];
[otherValues removeObjectAtIndex:0];
[subscriber sendNext:tuple];
sendCompletedIfNecessary();
}
};
//signal1被訂閱
[self subscribeSavingDisposable:^(RACDisposable *disposable) {
[subscriber.disposable addDisposable:disposable];
} next:^(id x) {
@synchronized (selfValues) {
[selfValues addObject:x ?: RACTupleNil.tupleNil];
sendNext();
}
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
@synchronized (selfValues) {
selfCompleted = YES;
sendCompletedIfNecessary();
}
}];
//signal2被訂閱
[signal subscribeSavingDisposable:^(RACDisposable *disposable) {
[subscriber.disposable addDisposable:disposable];
} next:^(id x) {
@synchronized (selfValues) {
[otherValues addObject:x ?: RACTupleNil.tupleNil];
sendNext();
}
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
@synchronized (selfValues) {
otherCompleted = YES;
sendCompletedIfNecessary();
}
}];
}] setNameWithFormat:@"[%@] -zipWith: %@", self.name, signal];
}
該方法包含如下步驟:
1、維護selfValues和otherValues兩個數組,里面分別包含signal1和signal2發出的信號數據,以及selfCompleted和otherCompleted兩個變量,表示信號響應是否完成。
2、signal1訂閱,觸發next事件,將"test1"加入selfValues,觸發sendNext()方法,由于此時otherValues尚沒有數據,直接return。signal1的觸發subscriber的completed事件,selfCompleted變為YES。
3、signal2訂閱,觸發next事件,將"test2"加入otherValues,觸發sendNext()方法,將selfValues和otherValues數組中的第一個元素去除,拼成一個RACTuple對象,作為信號數據傳遞給subscriber的next事件,輸出tuple中的各個元素。
4、signal2觸發completed事件,otherCompleted變為YES。調用sendCompletedIfNecessary(),由于selfCompleted或者otherCompleted變量置為Yes,且selfValues和otherValues數組中元素為空,觸發subscriber的completed事件,輸出"completed"。