本文通过一个仅有几行的demo,来分析一下它后面的执行过程。
demo是这个样子的
- (void)next:(id)subscriber {
[subscriber sendNext:@"step1"];
[subscriber sendCompleted];
}
- (RACSignal *)signInSignal {
__weak typeof(self) weakSelf = self;
return [RACSignal createSignal:^RACDisposable *(id subscriber){
[weakSelf performSelector:@selector(next:) withObject:subscriber afterDelay:2];
return [RACDisposable disposableWithBlock:^{
}];
}];
}
- (void)testRAC {
RACSignal *signal1 = [self signInSignal];
RACSignal *signal2 = [signal1 map:^(NSString *value){
return [value stringByAppendingString:@"-stpe2"];
}];
[signal2 subscribeNext:^(id value){
NSLog(@"value = %@",value);
CFRunLoopStop(CFRunLoopGetCurrent());
}];
CFRunLoopRun();
}
用过RAC的一定不陌生这几个方法,createSignal创建一个信号,map转换信号,订阅信号。看一眼就知道输出结果是
2016-07-20 22:32:36.388 RACDemo[86900:1804226] value = step1-stpe2
这个demo可以分两个过程:创建和订阅信号(1-3)和发送信号(4-7)
- 自定义一个block创建signal1,block被设置为signal1的didSubscribe变量。
RACDynamicSignal创建信号
+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
-
map方法传入block创建一个新的信号signal2,通过map创建会用到bind方法,就是FRP编程中的monad把一个方法应用到一个上下文中再返回一个上下文。
RACSignal - (RACSignal *)bind:(RACStreamBindBlock (^)(void))block { return [[RACSignal createSignal:^(id
subscriber) { ... }] setNameWithFormat:@"[%@] -bind:", self.name]; } -
订阅signal2,调用didSubscribe方法。因为signal2是在signal1内创建的所以这时也可以拿到signal1,接着订阅signal1,就调用到了demo里的signal1创建时signInSignal方法内的block。整个过程可以总结为:最后一个信号被订阅时,反向去往前订阅,直到订阅到第一个信号。每次订阅都会调用didSubscribe即创建时的block,每次在该方法内又会订阅前一个信号就是创建它的信号,这样就可以一直调到第一个信号的didSubscribe。
RACSignal RACStreamBindBlock bindingBlock = block(); //接收信号值value转换信号值后,返回新的信号 @autoreleasepool { RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init]; [compoundDisposable addDisposable:selfDisposable]; //订阅原信号 添加map/filter等操作的信号 RACDisposable *bindingDisposable = [self subscribeNext:^(id x) { // Manually check disposal to handle synchronous errors. if (compoundDisposable.disposed) return; BOOL stop = NO; id signal = bindingBlock(x, &stop);//中间信号 @autoreleasepool { if (signal != nil) addSignal(signal);//对中间信号订阅等 if (signal == nil || stop) { [selfDisposable dispose]; completeSignal(self, selfDisposable); } } } error:^(NSError *error) { [compoundDisposable dispose]; [subscriber sendError:error]; } completed:^{ @autoreleasepool { completeSignal(self, selfDisposable); } }]; selfDisposable.disposable = bindingDisposable; } - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock { NSCParameterAssert(nextBlock != NULL); NSCParameterAssert(errorBlock != NULL); NSCParameterAssert(completedBlock != NULL); //封装外部传入操作信号值的block RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock]; return [self subscribe:o]; } RACDynamicSignal - (RACDisposable *)subscribe:(id
)subscriber { NSCParameterAssert(subscriber != nil); RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable]; //生成Passthrough信号,保存订阅者和当前信号 if (self.didSubscribe != NULL) { RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ RACDisposable *innerDisposable = self.didSubscribe(subscriber); //执行信号创建时的block [disposable addDisposable:innerDisposable]; }]; [disposable addDisposable:schedulingDisposable]; } return disposable; } -
在demo的next:方法内,调用sendNext:会发送信号。subscriber参数是在订阅时创建的,封装了订阅时传入的block
RACPassthroughSubscriber - (void)sendNext:(id)value { if (self.disposable.disposed) return; if (RACSIGNAL_NEXT_ENABLED()) { RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description])); } //RACPassthroughSubscriber 向创建时传入的订阅者发送信号 这里包装了一层 [self.innerSubscriber sendNext:value]; } RACSubscriber - (void)sendNext:(id)value { @synchronized (self) { void (^nextBlock)(id) = [self.next copy]; if (nextBlock == nil) return; //订阅时传入的block nextBlock(value); } }
通过上面的第3步得知,signal1是在signal2创建时的didSubscribe内被订阅的。发送信号后会执行订阅的方法,在该方法内会执行signal2创建时map传入的block得到一个新的信号值并再把它封装成信号。整个过程可以总结为:向订阅者发送信号后,会调用转换新号(map或filter)的block方法,生成封装了计算信号值结果的中间信号。
-
订阅中间信号,中间信号被订阅后会直接发送信号,收到信号后会向signal2的订阅者发送信号,就调到了demo中signal2的subscribeNext:方法传入的block内。整个过程可以总结为:订阅中间信号,中间信号把信号值带出来,原信号用这个信号值发送信号。
RACSignal void (^addSignal)(RACSignal *) = ^(RACSignal *signal) { @synchronized (signals) { [signals addObject:signal]; } RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init]; [compoundDisposable addDisposable:selfDisposable]; //订阅中间信号,ReturnSignal会直接执行sendNext:发送信号进入下面这个block RACDisposable *disposable = [signal subscribeNext:^(id x) { //向当前信号的订阅者发送信号 [subscriber sendNext:x]; } error:^(NSError *error) { [compoundDisposable dispose]; [subscriber sendError:error]; } completed:^{ @autoreleasepool { completeSignal(signal, selfDisposable); } }]; selfDisposable.disposable = disposable; };
整个发送信号的过程会先计算信号值,然后生成新的中间信号,再向真正的订阅者发送中间信号的信号值。
思考中间信号:中间信号封装了RACSubscriber,它负责转发所有事件给订阅者。有了这一层封装,可以扩展取消等操作。
可以看出整个过程有几个关键角色
RACSginal 创建信号
创建信号会调用RACDynamicSignal的createSignal方法,传入一个block作为didSubscribe参数。RACDynamicSignal会copy这个block到subscriber属性。
RACSubscriber 订阅者
订阅者在订阅subscribeNext:方法调用时生成,把传入的block封装在RACSubscriber对象中。
subscribe 订阅信号
添加订阅后自动触发订阅subscribe:操作,将上一步生成的RACSubscriber作为参数传递进去。
在subscribe方法内部首先把传入的订阅者subscriber和信号自己self封装到RACPassthroughSubscriber转移信号中,再把转移信号传入didSubscribe的block中执行,完成订阅信号。
sendNext 发送信号
发送信号在转移信号内,执行把传入的信号值value发送给订阅者。订阅者调用netxBlock把value值送出,即送到了订阅时传入的block,返回值再生成中间信号供下一步继续订阅,如此反复将信号值发送到最后一个订阅者。
整体过程有点绕,但RAC都为我们包装在了里面。整体看下来,在外层我们使用时,会对信号做一层层处理比如用map或filter方法,最后订阅生成的最终信号。在订阅后内部发生一个反向回去的订阅过程,最终调用到第一个信号创建时传入的block。接下来发送信号后就是一层层的传递下来,每个信号计算之后会生成一个中间信号,对中间信号订阅后马上发信号给下一个信号,让信号值再一直传递下来,直到我们外层传入的订阅block。