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<RACSubscriber>)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<RACSubscriber> 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<RACSubscriber> subscriber) {
[subscriber sendNext:value];
}];
}
总结
- 创建的subject的是内部会创建一个数组_subscribers用来保存所有的订阅者
- 订阅信息的时候会创建订阅者,并且保存到数组中
- 遍历subject中_subscribers中的订阅者,依次发送信息
注意
RACSubject 可以被订阅多次,并且只能是先订阅后发布, 因为:
先发送, 再有订阅者, 订阅者收不到订阅之前的消息, 所以称之为先来先得, 晚来少得
- RACReplaySubject
针对RACSubject 的注意点, 偏偏要先发送消息, 再去订阅信号, 该怎么办呢???
这里就可以使用RACReplaySubject ,
它继承自RACSubject
, 目的就是来解决先发送信号后订阅的问题.
代码分析: 和RACSubject 的使用一毛一样
// 先发送信号
[replaySubject sendNext:@10];
// 后订阅信号
[replaySubject subscribeNext:^(id _Nullable x) {
// 可以正常打印
NSLog(@"x = %@", x);
}];
↓具体实现原理↓
- RACReplaySubject 对象创建的时候, 会在父类的基础之上多做一步,创建一个数组用来保存发送的数据(当_subscribers 中所有订阅者都成功发送了数据, 那么就会删除当前要发送的数据, 避免出现一个数据重复发送的问题)
- 发送数据: 当有订阅者订阅时,发送数据; 没有订阅者订阅时发送失败, 就不发送,等待新的订阅者.
- 订阅信号, 先遍历一次保存数据的数组, 如果有就执行第二步
.End