RACSubject、RACReplaySubject(内附冷信号和热信号的区别)

文章系列
《ReactiveCocoa 概述》
《RACSignal》
《RACDisposable》
《RACSubject、RACReplaySubject(内附冷信号和热信号的区别)》
《集合RACTuple、RACSequence》
《RAC 中的通知、代理、KVO, 基本事件、方法的监听》
《rac_liftSelector》
《RACMulticastConnection》
《RACCommand》
《RAC - 核心方法bind》
《RAC - 定时器》
《RACScheduler》
《RAC - 点击获取验证码 demo》
《RAC - 映射(Map & flattenMap)》
《RAC信号操作解释合集》
《RAC - 信号的生命周期》

  • RACSubject

RACSignal 是冷信号, 不能够自己发送信号, 需要订阅者订阅, 特点是确定未来, 也就是知道什么时候结束/终止, 无视订阅者, 不管谁订阅, 都是从头开始执行一段老代码

帮助理解: 冷信号 相当于 剧本, 当订阅者订阅时, 就相当于开始拍戏, 不管谁订阅, 都是从头开始拍, 拍完了也就结束了.

RACSubject 继承自RACSignal, 是热信号, 也就是说既可以充当信号,也可以发送信号, 并不确定什么时候终止, 关心订阅者, 先来先得、后来少得

帮助理解: 热信号 相当于 拍好的戏, 当订阅者订阅时, 就开始演戏, 接下来再有订阅者订阅, 演到哪里就继续演, 不会重新从头开始, 即先来的看得多、后来看的就少.

代码分析:

    // 1.创建信号
    RACSubject *subject = [RACSubject subject];
    
    // 2.订阅信号
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"x = %@", x);
    }];
    
    // 3.发送数据
    [subject sendNext:@10];

↓[RACSubject subject] 内部实现↓

+ (instancetype)subject {
    return [[self alloc] init];
}

- (instancetype)init {
    self = [super init];
    if (self == nil) return nil;

    // 创建一个 复合的_disposable 对象, 用于取消订阅
    _disposable = [RACCompoundDisposable compoundDisposable];
    // 创建一个 _subscribers 订阅者集合, 用来保存订阅者
    _subscribers = [[NSMutableArray alloc] initWithCapacity:1];
    
    return self;
}

↓ 订阅信号 subscribeNext: 内部实现↓
其实内部也RACSignal 信号订阅大致相同, 唯一不同的是subscribe: 方法的实现:

- (RACDisposable *)subscribe:(id)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

    NSMutableArray *subscribers = self.subscribers;
    @synchronized (subscribers) {
        // 注意: 每次订阅, 都将订阅者添加至 信号内部的_subscribers 数组中
        [subscribers addObject:subscriber];
    }
    
    [disposable addDisposable:[RACDisposable disposableWithBlock:^{
        @synchronized (subscribers) {
            // Since newer subscribers are generally shorter-lived, search
            // starting from the end of the list.
            NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id obj, NSUInteger index, BOOL *stop) {
                return obj == subscriber;
            }];

            if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
        }
    }]];

    return disposable;
}

↓ 发布消息sendNext: 内部实现↓

- (void)sendNext:(id)value {
    // 遍历_subscribers 集合, 向每一个订阅者发送sendNext: 消息, 即所有订阅者依次发送消息
    [self enumerateSubscribersUsingBlock:^(id subscriber) {
        [subscriber sendNext:value];
    }];
}
总结
  1. 创建的subject的是内部会创建一个数组_subscribers用来保存所有的订阅者
  2. 订阅信息的时候会创建订阅者,并且保存到数组中
  3. 遍历subject中_subscribers中的订阅者,依次发送信息

注意

RACSubject 可以被订阅多次,并且只能是先订阅后发布, 因为:先发送, 再有订阅者, 订阅者收不到订阅之前的消息, 所以称之为先来先得, 晚来少得
  • RACReplaySubject
    针对RACSubject 的注意点, 偏偏要先发送消息, 再去订阅信号, 该怎么办呢???

这里就可以使用RACReplaySubject , 它继承自RACSubject, 目的就是来解决先发送信号后订阅的问题.

代码分析: 和RACSubject 的使用一毛一样

    // 先发送信号
    [replaySubject sendNext:@10];
    
    // 后订阅信号
    [replaySubject subscribeNext:^(id  _Nullable x) {
        // 可以正常打印
        NSLog(@"x = %@", x);
    }];

↓具体实现原理↓

  1. RACReplaySubject 对象创建的时候, 会在父类的基础之上多做一步,创建一个数组用来保存发送的数据(当_subscribers 中所有订阅者都成功发送了数据, 那么就会删除当前要发送的数据, 避免出现一个数据重复发送的问题)
  2. 发送数据: 当有订阅者订阅时,发送数据; 没有订阅者订阅时发送失败, 就不发送,等待新的订阅者.
  3. 订阅信号, 先遍历一次保存数据的数组, 如果有就执行第二步

.End

你可能感兴趣的:(RACSubject、RACReplaySubject(内附冷信号和热信号的区别))