ReactiveCocoa 学习笔记二(RACSignal)

RACSignal

创建实例方法

RACSignal 是 RACStream 的子类,它提供了创建信息流实例的类方法。

  1. createSignal:

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

    该方法最为常用,实际是调用了 RACSignal 的子类 RACDynamicSignal 的方法创建了一个信息流实例对象,输入参数是一个代码块,从参数名称 didSubscibe 可以理解其是在该信号流被订阅时调用的,调用时,会将遵循了协议 <RACSubscriber> 的 subscriber 订阅者作为参数传入,而返回 RACDisposable 实例用于不再订阅该信号流时的清理工作。

    • RACDynamicSignal

      子类 RACDynamicSignal 的工作很重要,其提供了一个创建实例的方法,如下:

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

      这个方法将传递的代码块参数进行复制保留,以待合适的时机进行执行,而时机就在其重写的 subscribe: 方法中。

      - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
          NSCParameterAssert(subscriber != nil);
      
          RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
          subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber
                                       signal:self disposable:disposable];
      
          if (self.didSubscribe != NULL) {
              RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
                  RACDisposable *innerDisposable = self.didSubscribe(subscriber);
                  [disposable addDisposable:innerDisposable];
              }];
      
              [disposable addDisposable:schedulingDisposable];
          }
      
          return disposable;
      }

      该方法是父类抽象方法的具体实现,每当有订阅者订阅信号流时,便会调用该方法,其所执行的主要任务就是执行创建当前信号流时所传递的 didSubscribe 代码块,另外,便是将代码块返回的 innerDisposable 实例添加到 disposable 中,而 disposable 则被订阅者 subscriber 所持有,一旦信号流产生结束或者错误信号,那么便可以通过其所持有的 disposable 进行清理工作。

      从上面的代码可知,RACSubscriber 类型的订阅者 subscriber 会被进一步封装为 RACPassthroughSubscriber 实例对象,当然,两者都遵循 <RACSubscriber> 协议。

  2. error:

    + (RACSignal *)error:(NSError *)error {
        return [RACErrorSignal error:error];
    }
    • RACErrorSignal

      该类创建一个持有指定错误 error 信息的 RACErrorSignal 实例,当订阅者订阅该信号流时,则立即发送该错误信号量,并且订阅者随之结束对信号流的订阅。

      - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
          NSCParameterAssert(subscriber != nil);
      
          return [RACScheduler.subscriptionScheduler schedule:^{
              [subscriber sendError:self.error];
          }];
      }

      从这个重写方法可知,订阅者订阅该信号流时,只会接收到一次信号量,并且订阅时并不会执行类似 didSubscribe 的自定义操作(创建时就没有传递该参数),如果要再次获取该错误信息,则需要重新订阅该信号流。

  3. never

    + (RACSignal *)never {
        return [[self createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
            return nil;
        }] setNameWithFormat:@"+never"];
    }

    never 创建了一个永远不会产生信号量的流,它是 RACDynamicSignal 实例对象。

  4. startLazilyWithScheduler:block:

    + (RACSignal *)startLazilyWithScheduler:(RACScheduler *)scheduler
                                     block:(void (^)(id<RACSubscriber> subscriber))block {
        NSCParameterAssert(scheduler != nil);
        NSCParameterAssert(block != NULL);
    
        RACMulticastConnection *connection = [[RACSignal
            createSignal:^ id (id<RACSubscriber> subscriber) {
                block(subscriber);
                return nil;
            }]
            multicast:[RACReplaySubject subject]];
    
        return [[[RACSignal
            createSignal:^ id (id<RACSubscriber> subscriber) {
                [connection.signal subscribe:subscriber];
                [connection connect];
                return nil;
            }]
            subscribeOn:scheduler]
            setNameWithFormat:@"+startLazilyWithScheduler: %@ block:", scheduler];
    }

    为了便于说明,将上述代码进行如下拆分:

    + (RACSignal *)startLazilyWithScheduler:(RACScheduler *)scheduler 
                                block:(void (^)(id<RACSubscriber> subscriber))block {
        NSCParameterAssert(scheduler != nil);
        NSCParameterAssert(block != NULL);
    
        RACSignal *signal_1 = [RACSignal
                               createSignal:^ id (id<RACSubscriber> subscriber) {
                                   block(subscriber);
                                   return nil;
                               }];
    
        RACSubject *signal_2 = [RACReplaySubject subject];
        RACMulticastConnection *connection = [signal_1 multicast:signal_2];
    
        RACSignal *signal_3 = [RACSignal
                               createSignal:^ id (id<RACSubscriber> subscriber) {
                                   [connection.signal subscribe:subscriber];
                                   [connection connect];
                                   return nil;
                               }];
    
        RACSignal *signal_4 = [signal_3 subscribeOn:scheduler];
    
        return [signal_4 setNameWithFormat:@"+startLazilyWithScheduler: %@ block:", scheduler];
    }

    可知,在使用该方法时,最终得到的信号流是 signal_4 ,那么参见 subscribeOn: 方法可知,每当订阅者订阅信号流 signal_4 时,都会使得 signal_3 的 subscribe 方法调用。

    - (RACSignal *)subscribeOn:(RACScheduler *)scheduler {
        return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
            RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    
            RACDisposable *schedulingDisposable = [scheduler schedule:^{
                RACDisposable *subscriptionDisposable = [self subscribe:subscriber];
    
                [disposable addDisposable:subscriptionDisposable];
            }];
    
            [disposable addDisposable:schedulingDisposable];
            return disposable;
        }] setNameWithFormat:@"[%@] -subscribeOn: %@", self.name, scheduler];
    }

    那就意味着下面的代码被执行:

    [connection.signal subscribe:subscriber];
    [connection connect];

    connection.signal 就是 signal_2 ,其是 RACReplaySubject 类型,既可以是信号流又可以是订阅者,其会将接收到的所有信号量保存,每当被订阅时,其会将保存的信号量依次发给这个新的订阅者,所以这是个可以回放信号量的信号流。

    接着就是 connect 方法的调用,参见下面的代码:

    - (RACDisposable *)connect {
        BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);
    
        if (shouldConnect) {
            self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
        }
    
        return self.serialDisposable;
    }

    这个 self.sourceSignal 就是 signal_1 ,而对其订阅会使得其所持有的 didSubscribe 代码块执行,并且从 connect 方法可以知道其只执行一次,那便表示最初的 block 代码只会执行一次。

    综上所述,startLazilyWithScheduler:block: 方法的调用会获取一个信号流,该信号流会重播 signal_2 信号流的信号量,而能接收多少信号量,关键在于 block 中产生了多少信号量,如下面的例子:

    self.signal = [RACSignal startLazilyWithScheduler:RACScheduler.immediateScheduler 
                                    block:^(id<RACSubscriber>  _Nonnull subscriber) {
    
              //其实这个 subscriber 就是 signal_2
            [subscriber sendNext:@"replay-0"];
            [subscriber sendNext:@"replay-1"];
            [subscriber sendCompleted];
        }];
    
    [self.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"x = %@",x);
        }];    

    打印结果:

    2018-08-02 19:24:33.948200+0800 testP[3785:206370] x = replay-0
    2018-08-02 19:24:33.948405+0800 testP[3785:206370] x = replay-1
  5. startEagerlyWithScheduler:block:

    + (RACSignal *)startEagerlyWithScheduler:(RACScheduler *)scheduler 
                            block:(void (^)(id<RACSubscriber> subscriber))block {
        NSCParameterAssert(scheduler != nil);
        NSCParameterAssert(block != NULL);
    
        RACSignal *signal = [self startLazilyWithScheduler:scheduler block:block];
        // Subscribe to force the lazy signal to call its block.
        [[signal publish] connect];
        return [signal setNameWithFormat:@"+startEagerlyWithScheduler: %@ block:", scheduler];
    }
    
    - (RACMulticastConnection *)publish {
        RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
        RACMulticastConnection *connection = [self multicast:subject];
        return connection;
    }

    这个方法同上一个方法类似,区别在于该方法创建的信号流会直接触发 block 的执行,当然该 block 也是只执行一次,每当对返回的信号流进行订阅时,就会重播 block 中产生的信号量。

    值得注意的是,RACSubject 会将所有的订阅者保存(上一个方法也是一样),并且每当产生一个信号量时,该信号量会被发送给所有的订阅者,所以当订阅者不再需要时,应将其清理,避免其接收额外的信号量。

    如下例子,如果不进行清理,随着按钮的点击,信号量将重复打印。

    self.eagerSignal = [RACSignal startEagerlyWithScheduler:RACScheduler.immediateScheduler 
                                                block:^(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"replay-0"];
            [subscriber sendNext:@"replay-1"];
            [NSTimer scheduledTimerWithTimeInterval:6 repeats:YES block:^(NSTimer * _Nonnull timer) {
                [subscriber sendNext:@"replay-3"];
    
            }];
        }];
    
    //每一次点击,都将多出一个订阅者
    - (IBAction)click:(id)sender {
        [self.eagerSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"x = %@",x);
        }];
    }
    
    //产生新的订阅者前,先将原订阅者清理
    - (IBAction)click:(id)sender {
    [self.disposable dispose];
        self.disposable = [self.eagerSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"x = %@",x);
        }];
    }

重写方法

  1. empty

    该方法返回一个 RACEmptySignal 共享实例对象,该信号流不同于 never 方法返回的信号流,前者被订阅时会立即产生结束信号量,而后者不会产生信号量。

  2. return:

    该方法返回一个 RACReturnSignal 信号流,该信号流被订阅时会立即产生一个包含一个指定值的信号量,而后产生结束信号量。

  3. bind:

    学习 RACStream 类可知,其几乎所有的方法最终都会调用 bind: 方法,所以理解该方法的工作对于理解其他方法至关重要。

    typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);

    该类型包含两个参数和一个信号流返回值。

    参见 bind: 方法的代码,为便于分析,进行了调整。

    - (RACSignal *)bind:(RACSignalBindBlock (^)(void))block {
        NSCParameterAssert(block != NULL);
    
        RACSignal *signal_1 = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
            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) {
                    [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;
                    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;
        }];
    
        return [signal_1 setNameWithFormat:@"[%@] -bind:", self.name];
    }

    该方法涉及 3 个信号流,调用该 bind: 方法的源信号流 signal_0,该方法返回的新信号流 signal_1,以及 block 代码块返回值的调用所返回的信号流 signal

    从执行过程来解析,当返回的 signal_1 信号流被订阅时,bind: 的参数 block 将被调用,那么便意味着 signal_0 源信号流被订阅,接着便是 signal_0 产生信号流,nextBlock 被调用,从而 bindingBlock 被调用,返回 signal 信号流,如下代码片段:

    BOOL stop = NO;
                    id signal = bindingBlock(x, &stop);
    
                    @autoreleasepool {
                        if (signal != nil) addSignal(signal);
                        if (signal == nil || stop) {
                            [selfDisposable dispose];
                            completeSignal(selfDisposable);
                        }
                    }

    可见,如果 signal 存在,那么便会调用下面的代码:

    RACDisposable *disposable = [signal subscribeNext:^(id x) {
                    [subscriber sendNext:x];
                } error:^(NSError *error) {
                    [compoundDisposable dispose];
                    [subscriber sendError:error];
                } completed:^{
                    @autoreleasepool {
                        completeSignal(selfDisposable);
                    }
                }];

    可见,其对 signal 进行了订阅,并且会将接收到的信号量传递给 bind: 方法返回的信号流 signal_1 的订阅者,结合父类的 map: 方法来理解,可知上面的 signal 被订阅时,立即就接收到了信号量,从而传递给订阅者了。

    - (__kindof RACStream *)map:(id (^)(id value))block {
        NSCParameterAssert(block != nil);
    
        Class class = self.class;
    
        return [[self flattenMap:^(id value) {
            return [class return:block(value)];
        }] setNameWithFormat:@"[%@] -map:", self.name];
    }
    
    - (__kindof RACStream *)flattenMap:(__kindof RACStream * (^)(id value))block {
        Class class = self.class;
    
        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];
    }

    综上所述,可以对 bind: 这样理解,其将参数 block 的返回值返回的 signal 信号量绑定到调用该方法的 signal_0 源信号流,前者的信号量总是来自后者,而其又会将信号量转发给订阅了 bind: 方法返回的 signal_1 信号流的订阅者。

  4. concat:

    拼接信号流,将提供的参数信号流拼接在源信号流的后面,当源信号流结束传递信号量后便开始接收参数信号流的信号量,这里源信号流停止接收必须是正常的停止,不能是因为错误引起的。

    - (RACSignal *)concat:(RACSignal *)signal {
        return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
            RACCompoundDisposable *compoundDisposable = [[RACCompoundDisposable alloc] init];
    
            RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
                [subscriber sendNext:x];
            } error:^(NSError *error) {
                [subscriber sendError:error];
            } completed:^{
                RACDisposable *concattedDisposable = [signal subscribe:subscriber];
                [compoundDisposable addDisposable:concattedDisposable];
            }];
    
            [compoundDisposable addDisposable:sourceDisposable];
            return compoundDisposable;
        }] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
    }

    该方法涉及三个信号流,首先源信号流调用该方法创建的信号流是给用户使用的,当用户订阅该信号流时,便会导致源信号流被订阅,其接收到的信号量会被直接转发给新创建的信号流的订阅者,最终源信号流结束传递信号量时,参数信号流会被订阅者订阅,从而使得整个流程看起来像是两个信号流的拼接。

  5. zipWith:

    将源信号流中的一个信号量同参数信号流中的一个信号量封装为一个信号量通过该方法返回的新的信号流进行传递。组合的信号量必须是来自双方新的信号量,依序对应,只有两个信号流都有信号量时,才会产生一个新的信号量,如果任意一个信号流结束传递,那么该信号流也随之关闭。

    - (RACSignal *)zipWith:(RACSignal *)signal {
        NSCParameterAssert(signal != nil);
    
        return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
            __block BOOL selfCompleted = NO;
            NSMutableArray *selfValues = [NSMutableArray array];
    
            __block BOOL otherCompleted = NO;
            NSMutableArray *otherValues = [NSMutableArray array];
    
            void (^sendCompletedIfNecessary)(void) = ^{
                @synchronized (selfValues) {
                    BOOL selfEmpty = (selfCompleted && selfValues.count == 0);
                    BOOL otherEmpty = (otherCompleted && otherValues.count == 0);
                    if (selfEmpty || otherEmpty) [subscriber sendCompleted];
                }
            };
    
            void (^sendNext)(void) = ^{
                @synchronized (selfValues) {
                    if (selfValues.count == 0) return;
                    if (otherValues.count == 0) return;
    
                    RACTuple *tuple = RACTuplePack(selfValues[0], otherValues[0]);
                    [selfValues removeObjectAtIndex:0];
                    [otherValues removeObjectAtIndex:0];
    
                    [subscriber sendNext:tuple];
                    sendCompletedIfNecessary();
                }
            };
    
            RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
                @synchronized (selfValues) {
                    [selfValues addObject:x ?: RACTupleNil.tupleNil];
                    sendNext();
                }
            } error:^(NSError *error) {
                [subscriber sendError:error];
            } completed:^{
                @synchronized (selfValues) {
                    selfCompleted = YES;
                    sendCompletedIfNecessary();
                }
            }];
    
            RACDisposable *otherDisposable = [signal subscribeNext:^(id x) {
                @synchronized (selfValues) {
                    [otherValues addObject:x ?: RACTupleNil.tupleNil];
                    sendNext();
                }
            } error:^(NSError *error) {
                [subscriber sendError:error];
            } completed:^{
                @synchronized (selfValues) {
                    otherCompleted = YES;
                    sendCompletedIfNecessary();
                }
            }];
    
            return [RACDisposable disposableWithBlock:^{
                [selfDisposable dispose];
                [otherDisposable dispose];
            }];
        }] setNameWithFormat:@"[%@] -zipWith: %@", self.name, signal];
    }

    从源码中可知,当该方法返回的信号流被订阅时,源信号流和参数信号流都会被订阅,并且他们传递的信号量都会被保存,当能够组合为一个新的信号量传递给创建的信号流时,被保存的两个信号量便会从数组中删除,而当任意一个信号流结束时,订阅者便随之结束监听信号流。

订阅方法

信号流中的订阅方法十分重要,她决定着接收到信号量的订阅者所要执行的操作。通常情况下,我们只需要使用信号流提供的订阅方法,指定接收到某种信号量后所需要执行的代码块即可。因为,在框架中会自动创建一个 RACSubscriber 实例对象作为订阅者,该对象持有我们指定的操作代码块。而后,信号流持有的 didSubscribe 代码块会被执行,该订阅者对象会作为该代码块的参数进行传递。

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCAssert(NO, @"This method must be overridden by subclasses");
    return nil;
}

这个订阅方法是内部方法,RACSignal 的所有子类必须重写她,因为其他的订阅方法都会调用该方法。一般要指定接收到普通信号量后的操作、结束信号量的操作以及错误信号量的操作,几需要提供三个代码块,常用的方法如下:

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);

    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock {
    NSCParameterAssert(nextBlock != NULL);
    NSCParameterAssert(completedBlock != NULL);

    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:completedBlock];
    return [self subscribe:o];
}

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
    NSCParameterAssert(nextBlock != NULL);
    NSCParameterAssert(errorBlock != NULL);
    NSCParameterAssert(completedBlock != NULL);

    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
    return [self subscribe:o];
}

当然,三个参数的组合有7种,所以有7个方法与之对应。

我们常说信号流结束,其实质是订阅该信号流的订阅者实例被清理了,并不是意味着信号流不能够再传递信号量了,如果该信号流被显示持有,那么可以再次订阅该信号流来达到信号量的监听。当然,不管是信号流还是信号量,都是抽象的概念,其实质是作为响应式编程中目标操作触发条件的纽带。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值