RAC bind

作者: 哦呵呵y | 来源:发表于2018-04-24 13:29 被阅读36次

    系列文章
    RACSignal,RACSubject,RACReplaySubject
    RAC bind
    RAC Merge flatten
    RACMulticastConnection
    RAC switchToLatest

    RAC 的核心操作思想,HOOK,而核心方法bind 就是hook使用的体现。

    1. 在oc中使用hook,可以在方法调用中,替换原方法的实现。rac中也是一样,可以利用hook的思想,在信号和订阅者之间,加入一层新的逻辑。
    2. 正常来说我们发送信号只有信号的订阅者才会接收到消息,而bind会将信号拦截过滤后发送到新的信号订阅者中。
    bind的使用:
        RACSubject *subject = [RACSubject subject];
        RACSignal *signal = [subject bind:^RACSignalBindBlock _Nonnull{
            NSLog(@"有订阅者订阅");        // 第二步  订阅信后会触发这里
    // MAKR_1 此block 用来过滤信号 在原信号订阅者中会调用
            return ^(id value, BOOL *stop) {   
                NSLog(@"收到原信号数据");     // 第四步  收到信号后,处理完,返回一个新的信号
                NSLog(@"过滤处理以后将新的信号返回出去")
                return [RACReturnSignal return:value];
            };
        }];
        [signal subscribeNext:^(id  _Nullable x) {    //第一步 订阅信号
            NSLog(@"收到最终的信号");
        }];
        [subject sendNext:@"发送信号"];    // 第三部  发送信号
    
    ...
    // bind 内部实现
        return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    // 当bind返回的信号被注册时会调用这里  获取到 bind传入的block用来对原数据进行包装生成新的信号
            RACSignalBindBlock bindingBlock = block();
    
            __block volatile int32_t signalCount = 1;   // indicates self
    
            RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
    
            void (^completeSignal)(RACDisposable *) = ^(RACDisposable *finishedDisposable) {
                if (OSAtomicDecrement32Barrier(&signalCount) == 0) {
                    [subscriber sendCompleted];
                    [compoundDisposable dispose];
                } else {
                    [compoundDisposable removeDisposable:finishedDisposable];
                }
            };
    
            void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
                OSAtomicIncrement32Barrier(&signalCount);
    
                RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
                [compoundDisposable addDisposable:selfDisposable];
    
                RACDisposable *disposable = [signal subscribeNext:^(id x) {
    // MARK_3  这里的  subscriber 还是bind返回信号的  订阅者  所以这里会出来外面对bind信号的订阅者消息
                    [subscriber sendNext:x];
                } error:^(NSError *error) {
                    [compoundDisposable dispose];
                    [subscriber sendError:error];
                } completed:^{
                    @autoreleasepool {
                        completeSignal(selfDisposable);
                    }
                }];
    
                selfDisposable.disposable = disposable;
            };
    
            @autoreleasepool {
                RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
                [compoundDisposable addDisposable:selfDisposable];
    // 对原信号的订阅  当原信号调用时  会触发这里
                RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
                    // Manually check disposal to handle synchronous errors.
                    if (compoundDisposable.disposed) return;
    
                    BOOL stop = NO;
    // MARK_2  包装后的信号
                    id signal = bindingBlock(x, &stop);
    
                    @autoreleasepool {
                        if (signal != nil) addSignal(signal);  
                        if (signal == nil || stop) {
                            [selfDisposable dispose];
                            completeSignal(selfDisposable);
                        }
                    }
                } error:^(NSError *error) {
                    [compoundDisposable dispose];
                    [subscriber sendError:error];
                } completed:^{
                    @autoreleasepool {
                        completeSignal(selfDisposable);
                    }
                }];
    
                selfDisposable.disposable = bindingDisposable;
            }
    
            return compoundDisposable;
        }] setNameWithFormat:@"[%@] -bind:", self.name];
    

    bind的操作流程

    1. 调用bind方法,会生成一个新的信号。
    2. 对新的信号进行订阅,会触发bind传入的block,这时bind的内部实现会去订阅原信号
    3. 原信号发送后,会触发,bind内部对原信号的订阅者block。这时会调用bind是内部block返回的用来过滤信号的block(对应MAKR_1的block)。
    4. 这是将原数据发送到 MAKR_1 处的block中,获取到一个包装后的信号。(MARK_2 信号 RACReturnSignal 类型)
    5. 然后将 MARK_2 信号进行订阅。而 RACReturnSignal 类型的信号重写了
    - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
        NSCParameterAssert(subscriber != nil);
    
        return [RACScheduler.subscriptionScheduler schedule:^{
            [subscriber sendNext:self.value];
            [subscriber sendCompleted];
        }];
    }
    

    所以当信号订阅时,会立马触发nextBlock

    1. MARK_2 订阅者中调用 bind 信号订阅者去发送信号,所以外部对bind信号订阅的nextBlock这时就会调用(MARK_3)
    2. 这是就完成了整个流程,bind后,原信号发送的消息,会经过bind时传入的block进行过滤,然后返回一个新的消息,发送的bind信号的。
      原信号->bind(生成新信号)
      原信号发送消息->bind Block 进行过滤,然后发送消息到内部一个信号中-> 转发到bind时生成的新信息号中

    RAC中运用了很多hook的思想,所以会非常的绕,其实本质有点类似消息转发,将原信号进行包装过滤,在发送到一个新的信号中。

        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];
    

    还有一个很重要的flattenMap方法,其实就是bind的封装。内部就是bind方法的调用,然后可以传入一个block 作为过滤的规则

    Vertical Cross Functional Template.png

    相关文章

      网友评论

        本文标题:RAC bind

        本文链接:https://www.haomeiwen.com/subject/ucxjlftx.html