美文网首页
ReactiveCocoa的bind方法

ReactiveCocoa的bind方法

作者: kenny肉桂 | 来源:发表于2017-06-17 21:41 被阅读183次

    RAC的Bind方法

    RAC提供了一堆可以提高开发效率的方法,比如filter,map,flattenMap等值处理方法,几乎每个方法点到底,都能看到一个叫做bind的方法.这个方法就是RAC相对底层的方法.弄明白它,对于理解RAC是非常有帮助的.

    现象

    - (void)runTest {
        // 1.
        RACSubject *subject = [RACSubject subject];
        // 2.
        RACSignal *bindSignal = [subject bind:^RACSignalBindBlock _Nonnull {
           
            return ^RACSignal *( id value, BOOL *stop ) {//这个block只要源信号发送数据,就会调用
                NSLog(@"接收到源信号的内容: %@",value);
                //2.1
                //返回信号,不能传nil,如果真的想返回空信号,用empty(其实就是一个alloc
                // init) . 如果返回empty,那就订阅链就断了.
               //  return [RACSignal empty];
                return [RACReturnSignal return:value];
            };
    
        }];
    
        // 3.
        [bindSignal subscribeNext:^( id _Nullable x ){
                //3.1
            NSLog(@"订阅的地方,也就是处理完的信号:%@",x);
        }];
        // 4.
        [subject sendNext:@"123"];
    }
    

    1.创建源信号
    2.通过bind得到绑定信号
    任何信号都能调用 bind 方法,bind方法需要一个RACSignalBindBlock类型的参数,这个类型定义typedef RACSignal * _Nullable (^RACSignalBindBlock)(id _Nullable value, BOOL *stop), 早期版本,返回值是 RACStream,现在是 RACSignal,其实都一样.RACSignal继承RACStream. 响应式编程中,万物皆是流
    3.订阅绑定信号
    3.1 如果2.1处返回的是 empty, 那么3.1处将不会执行.
    4.源信号发送数据

    从源码看

    1. 创建信号 RACSubject *subject = [RACSubject subject];
      RACSubject.m文件中
        - (instancetype)init {
        self = [super init];
        if (self == nil) return nil;
    
        _disposable = [RACCompoundDisposable compoundDisposable];
        _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
        
        return self;
        }
    

    _subscribers 这个是订阅者数组.

    1. 绑定信号 [subject bind:^RACSignalBindBlock _Nonnull{....}
      这个bind点进去,里面代码如下:
    
    - (RACSignal *)bind:(RACSignalBindBlock (^)(void))block {
        NSCParameterAssert(block != NULL);
    //没有 block() ,即没有执行参数block
    //2.1
        return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    //2.2
            RACSignalBindBlock bindingBlock = block();
            ...
    

    可以看到,在bind方法中,参数block到结束的return都没有去调用.bind返回了一个新创建的信号.这个在代码中,体现为RACSignal *bindSignal .
    而在 return后面的 [[RACSignal createSignal:^(id<RACSubscriber> subscriber),这个createSignal定义如下:

    + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
        return [RACDynamicSignal createSignal:didSubscribe];
    }
    

    可以看出,这个 createSignal:需要一个didSubscribe block,也就是在2.1处,传递的:

    ^(id<RACSubscriber> subscriber) {
            RACSignalBindBlock bindingBlock = block();
            ...
    
    

    继续看下去,[RACDynamicSignal createSignal:didSubscribe];这句中,createSignal的实现如下:

    + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
        RACDynamicSignal *signal = [[self alloc] init];
        signal->_didSubscribe = [didSubscribe copy];
        return [signal setNameWithFormat:@"+createSignal:"];
    }
    

    就是说,创建了一个 RACDynamicSignal类型的对象,然后将didSubscribe block赋值给它的_didSubscribe属性. 属性定义如下:

    @property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
    

    到此,这个bind流程走完了.

    总结就是:
    bind方法传递 RACSignalBindBlock 类型的参数到了 createSignal 的参数block中, createSingal 的参数block被保存为 RACDynamicSignal 的didSubscribe属性.

    注意,到此为止,没有任何block获得执行.也就是如果代码单纯如下:

        // 1.
        RACSubject *subject = [RACSubject subject];
        // 2.
        RACSignal *bindSignal = [subject bind:^RACSignalBindBlock _Nonnull {
        //breakpoint_1
            return ^RACSignal *( id value, BOOL *stop ) {
                NSLog(@"接收到源信号的内容: %@",value);
      //breakpoint_2
                return [RACReturnSignal return:value];
            };
    
        }];
    
    

    打在两个block中的两个breakpoint都不会执行.

    1. 订阅绑定信号
     [bindSignal subscribeNext:^( id _Nullable x ){
            NSLog(@"订阅的地方,也就是处理完的信号:%@",x);
        }];
    

    订阅信号,这个subscribeNext方法如下:

    - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
        NSCParameterAssert(nextBlock != NULL);
        //3.1
        RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
        //3.2
        return [self subscribe:o];
    }
    

    可以看到,方法内部创建了一个RACSubscriber订阅者.这个创建方法subscriberWithNext定义如下:

    + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
        RACSubscriber *subscriber = [[self alloc] init];
    
        subscriber->_next = [next copy];
        subscriber->_error = [error copy];
        subscriber->_completed = [completed copy];
    
        return subscriber;
    }
    

    可以看到,就是将对应的 next ,error,complete三种类型的block保存到订阅者中.在这里,我们的nextBlock就是:

    ^( id _Nullable x ){
            NSLog(@"订阅的地方,也就是处理完的信号:%@",x);
        }];
    

    也就是说,将我们的以上block保存到订阅者中.

    在3.1之后, 3.2 [self subscribe:o] 这是真正的去订阅信号.订阅信号,会触发信号的didSubscribe.也就是代码2.2所在的block会获得执行,而根据刚刚的线路.代码2.2中有RACSignalBindBlock bindingBlock = block();,也就是我们传递的返回值是RACSignalBindBlock类型的block会执行.也就是 breakPoint_1会执行 ,而代码中 block()的返回值是RACSignalBindBlock,代码将其保存到了bindingBlock这个变量中.
    在bind方法末尾:

        @autoreleasepool {
                RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
                [compoundDisposable addDisposable:selfDisposable];
                //3.3
                RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
                    // Manually check disposal to handle synchronous errors.
                    if (compoundDisposable.disposed) return;
    
                    BOOL stop = NO;
                    //3.4
                    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;
            }
            ```
    
    3.3 `RACDisposable *bindingDisposable = [self subscribeNext:^(id x)  ` self 代指的调用者,也就是我们的源信号.订阅这个信号,也是会创建一个订阅者.也是用来保存block的.以上代码中,nextBlock , errorBlock, completeBlock都有,都会被保存.
    而nextBlock做了:
    
    
    ```objc
    
            id signal = bindingBlock(x, &stop);
    
            @autoreleasepool {
            //4.5
                if (signal != nil) addSignal(signal);
                if (signal == nil || stop) {
                    [selfDisposable dispose];
                    completeSignal(selfDisposable);
                }
            }
    

    这样订阅流程就走完了.

    4.源信号发送数据 [subject sendNext:@"123"];
    这样会执行源信号订阅者的nextBlock,而nextBlock中有 bindingBlock的执行.bindingBlock有个x参数,这就是我们send的 @"123".
    注意4.5这里有个判空的过程:
    if (signal != nil) addSignal(signal);
    如果在2.2我们返回了空信号,那么addSignal(signal)是不会得到执行的.
    然后4.5这个bingBlock的返回值作为参数,传递到了addSignal中,addSignal的定义如下:

    void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
                OSAtomicIncrement32Barrier(&signalCount);
    
                RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
                [compoundDisposable addDisposable:selfDisposable];
    
                RACDisposable *disposable = [signal subscribeNext:^(id x) {
                    [subscriber sendNext:x];
                } error:^(NSError *error) {
                    [compoundDisposable dispose];
                    [subscriber sendError:error];
                } completed:^{
                    @autoreleasepool {
                        completeSignal(selfDisposable);
                    }
                }];
    
                selfDisposable.disposable = disposable;
            };
    

    内部,订阅了我们的处理后的信号(我们开始代码的bindSignal).这样新信号的didSubscribe block 就会得到执行.

    这就是使用bind进行了一次信号的完整流程.

    相关文章

      网友评论

          本文标题:ReactiveCocoa的bind方法

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