RAC的Subscription过程分析

本文通过一个仅有几行的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)

  1. 自定义一个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:"];
    }
  1. map方法传入block创建一个新的信号signal2,通过map创建会用到bind方法,就是FRP编程中的monad把一个方法应用到一个上下文中再返回一个上下文。

     RACSignal
     - (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
         return [[RACSignal createSignal:^(id subscriber) {
         ...
         }] setNameWithFormat:@"[%@] -bind:", self.name];
     }
    
  2. 订阅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;
     }
    
  3. 在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);
         }
     }
    
  4. 通过上面的第3步得知,signal1是在signal2创建时的didSubscribe内被订阅的。发送信号后会执行订阅的方法,在该方法内会执行signal2创建时map传入的block得到一个新的信号值并再把它封装成信号。整个过程可以总结为:向订阅者发送信号后,会调用转换新号(map或filter)的block方法,生成封装了计算信号值结果的中间信号。

  5. 订阅中间信号,中间信号被订阅后会直接发送信号,收到信号后会向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;
             };
    
  6. 整个发送信号的过程会先计算信号值,然后生成新的中间信号,再向真正的订阅者发送中间信号的信号值。

思考中间信号:中间信号封装了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。

你可能感兴趣的:(RAC的Subscription过程分析)