ReactiveCocoa 中signal(operation) then与doNext的区别
贴源码:
doNext:实现的主要源代码
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
block(x);
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}] setNameWithFormat:@"[%@] -doNext:", self.name];
then:实现的主要源代码
1、
- (RACSignal *)then:(RACSignal * (^)(void))block {
NSCParameterAssert(block != nil);
return [[[self
ignoreValues]
concat:[RACSignal defer:block]]
setNameWithFormat:@"[%@] -then:", self.name];
}
2、
- (RACSignal *)ignoreValues {
return [[self filter:^(id _) {
return NO;
}] setNameWithFormat:@"[%@] -ignoreValues", self.name];
}
3、
- (instancetype)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];
}
从上面doNext与then的代码比较
从实现上看有几点:
a、doNext是在当前信号执行的任务完成后,将当前任务的结果传递给新的signal,而then:则是执行完后不会把当前的singal的结果网下传,而是生成了一个empty这个signal接着让这个empty signal来处理接下来的事。(在信号本身订阅完后处理接下来的操作)
then是直接创建一个signal并让这个singal来转发当前消息执行后的结果, doNext到最后是flattenMap,flattenMap则是通过bind的方式来实现的,而bind的方法中有介绍其功能要求(处理完之前的订阅后生成一个empty信号然后在empty信号中执行接下来的操作。):
/*
* -bind: should:
*
* 1. Subscribe to the original signal of values.
* 2. Any time the original signal sends a value, transform it using the binding block.
* 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
* 4. If the binding block asks the bind to terminate, complete the _original_ signal.
* 5. When _all_ signals complete, send completed to the subscriber.
*
* If any signal sends an error at any point, send that to the subscriber.
*/
bind:其中2,3两点比较特别:
在任一时刻元信号收到一个处理值的时候bindblock会做一个转换处理;
bind将一个signal作为一个bindblock中的一个返回值并且订阅这个signal;
c、资源释放角度:
doNext:RACCompoundDisposable *disposable add RACDisposable *schedulingDisposable
then:RACCompoundDisposable *disposable add RACSerialDisposable *selfDisposable
d、使用doNext和then:
resetBtn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self)
RACSignal *signal = [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}] doNext:^(id x) {
NSLog(@"next");
}]doNext:^(id x) {
NSLog(@"next1");
}] then:^RACSignal *{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"then");
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}];
}];
return signal;
// }];
print:
next
next1
then
resetBtn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self)
RACSignal *signal = [[[[RACSignal empty] doNext:^(id x) {
NSLog(@"next");
}]doNext:^(id x) {
NSLog(@"next1");
}] then:^RACSignal *{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"then");
[subscriber sendNext:nil];
[subscriber sendCompleted];
return nil;
}];
}];
return signal;
// }];
}] ;
print:
then
then 执行的时候会忽略之前一个信号的结果,sendNext则会保留结果传给下个信号。
这样如果通过doNext:执行的话,如果执行的信号结果是一个empty那么doNext是不能够执行下去的。
而then的之前执行是在调用 ignoreValues 创建一个新的信号来接着执行,所以then能够确保下一步执行
提一下ignoreValues,ignoreValues这个方法最终实现是落在bind signal方法上,而bind这个方法实际上是一个串,通过里面的addSignal和completeBlock来完成。也就是说可以在doNext执行完之前都是信号依赖信号的形式,但是这种信号依赖机制不能够确保传出的不是empty信号,所以这种方式有可能会中断
也就是说then并不会把doNext的操作给忽略掉,只会把doNext的传出参数给忽略,同时doNext不能处理前一个signal为empty的情况,而then不管前一个signal是否是空的都可以接着执行
最后再看一下then和doNext的方法说明
then:
/// Ignores all `next`s from the receiver, waits for the receiver to complete,
/// then subscribes to a new signal.
doNext:
/// Do the given block on `next`. This should be used to inject side effects into
/// the signal.
前者忽略接收者的消息,并等待接收者执行完后,再订阅一个新的信号。
后者是将一个block作为副作用注入到signal中。
2017-01-09 17:26:23.355 HXViewDataBinding[2330:1268857] <RACDynamicSignal: 0x6000004269e0> name:
2017-01-09 17:26:23.355 HXViewDataBinding[2330:1268857] <RACDynamicSignal: 0x6180002241a0> name:
可以看到side effect其实也是通过createSignal的形式来完成的,只是在createsignal中完成了当前消息的转发并把消息结果给新的signal并让新的signal来做接下来的事