ReactiveCocoa的bind方法

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 subscriber) {
//2.2
        RACSignalBindBlock bindingBlock = block();
        ...

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

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

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

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

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

+ (RACSignal *)createSignal:(RACDisposable * (^)(id 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 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方法)