RACSubject
上文中,我们了解了RACSignal类创建信号订阅信号发送信号的过程分析。我们今天来观察另一个创建的方法,RACSubject创建信号。
RACSubject创建信号
RACSubject继承自RACSignal
@interface RACSubject : RACSignal <RACSubscriber>
/// Returns a new subject.
+ (instancetype)subject;
//1.Subject信号
RACSubject *subject = [RACSubject subject];
//2.订阅信号
[subject subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//3.发送信号
[subject sendNext:@"1"];
[subject sendCompleted];
与RACSignal不同的是,RACSubject是通过subject类方法来创建信号的。我们来看一下subject具体做了什么操作。
+ (instancetype)subject {
return [[self alloc] init];
}
通过调用自己的init方法返回当前类
- (id)init {
self = [super init];
if (self == nil) return nil;
/* 销毁 */
_disposable = [RACCompoundDisposable compoundDisposable];
/* 订阅者 */
_subscribers = [[NSMutableArray alloc] initWithCapacity:1];
return self;
}
这里面通过初始化disposable和subscribers。
_disposable = [RACCompoundDisposable compoundDisposable];
RACCompoundDisposable上次已经见过了,根据官方描述。主要是将disposable添加到RACCompoundDisposable中,这个过程是异步操作的(asynchronously)。
A disposable of disposables. When it is disposed, it disposes of all its contained disposables.
If -addDisposable: is called after the compound disposable has been disposed of, the given disposable is immediately disposed. This allows a compound disposable to act as a stand-in for a disposable that will be delivered asynchronously.
/// Creates and returns a new compound disposable.
+ (instancetype)compoundDisposable;
这里面调用了创建一个销毁类RACCompoundDisposable ,进一步深入分析。
+ (instancetype)compoundDisposable {
return [[self alloc] initWithDisposables:nil];
}
- (id)initWithDisposables:(NSArray *)otherDisposables {
self = [self init];
if (self == nil) return nil;
#if RACCompoundDisposableInlineCount
[otherDisposables enumerateObjectsUsingBlock:^(RACDisposable *disposable, NSUInteger index, BOOL *stop) {
_inlineDisposables[index] = disposable;
// Stop after this iteration if we've reached the end of the inlined
// array.
if (index == RACCompoundDisposableInlineCount - 1) *stop = YES;
}];
#endif
if (otherDisposables.count > RACCompoundDisposableInlineCount) {
_disposables = RACCreateDisposablesArray();
CFRange range = CFRangeMake(RACCompoundDisposableInlineCount, (CFIndex)otherDisposables.count - RACCompoundDisposableInlineCount);
CFArrayAppendArray(_disposables, (__bridge CFArrayRef)otherDisposables, range);
}
return self;
}
首先,值得关注是传入的参数,是一个数组,因此,可以大胆的猜测RACCompoundDisposable与RACDisposable不同之处是因为,前者利用数组存储多个RACDisposable,这一点我们会在后面证实。
虽然代码长,我们分为两部分分析,如果RACCompoundDisposableInlineCount存在则执行
[otherDisposables enumerateObjectsUsingBlock:^(RACDisposable *disposable, NSUInteger index, BOOL *stop) {
_inlineDisposables[index] = disposable;
// Stop after this iteration if we've reached the end of the inlined
// array.
//如果到达内联数组的末尾,请在此迭代后停止
if (index == RACCompoundDisposableInlineCount - 1) *stop = YES;
}];
否则执行直接执行下面语句。
RAC已经用定义
#define RACCompoundDisposableInlineCount 2
通过enumerateObjectsUsingBlock把disposable传给了自身的数组中。这一部分传入的是RACDisposable的数组
@interface RACCompoundDisposable () {
// Used for synchronization.
OSSpinLock _spinLock;
#if RACCompoundDisposableInlineCount
// A fast array to the first N of the receiver's disposables.
//
// Once this is full, `_disposables` will be created and used for additional
// disposables.
//
// This array should only be manipulated while _spinLock is held.
RACDisposable *_inlineDisposables[RACCompoundDisposableInlineCount];
#endif
// Contains the receiver's disposables.
//
// This array should only be manipulated while _spinLock is held. If
// `_disposed` is YES, this may be NULL.
CFMutableArrayRef _disposables;
// Whether the receiver has already been disposed.
//
// This ivar should only be accessed while _spinLock is held.
BOOL _disposed;
}
继续向下看,如果传入参数的大小大于定义的2(RACCompoundDisposableInlineCount)则执行
_disposables = RACCreateDisposablesArray();
CFRange range = CFRangeMake(RACCompoundDisposableInlineCount, (CFIndex)otherDisposables.count - RACCompoundDisposableInlineCount);
CFArrayAppendArray(_disposables, (__bridge CFArrayRef)otherDisposables, range);
很显然,这里面传入的是nil,因此,上面的代码是不会执行的。致此,第一步RACSubject创立完成。
RACSubject 发送信号
[subject sendNext:@"1"];
查看源码,不难发现发送信息,实际上是遍历了订阅者,并且让数组中每个订阅者发送消息。
- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
NSArray *subscribers;
@synchronized (self.subscribers) {
subscribers = [self.subscribers copy];
}
for (id<RACSubscriber> subscriber in subscribers) {
block(subscriber);
}
}
#pragma mark RACSubscriber
- (void)sendNext:(id)value {
[self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:value];
}];
}
思考:为什么使用同步锁???
与之前想法一致,当拷贝数组时候,防止出现线程问题,因此,采用同步锁阻塞线程,防止出现拷贝问题。在很多源码中,对于数组拷贝,数据更新时候都会采用同步锁。
RACSubject 接收信号
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
RACSubject 使用场景
此处接受和RACSignal相同的。
看到现在,你一定很疑惑,既然有RACSignal创建信号,为什么设计RACSubject在创建信号,岂不是多次一举???
首先,我们不需要像RACSignal利用block创建信号,同时,我们可以看到,RACSubject通过subject来创建(利用数组进行保存,逐一遍历订阅),同时,自身可以当作一个信号进行发送和订阅信号(而RACSignal订阅信号是通过RACSubscriber协议来订阅的)。
RACSubject *subject = [RACSubject subject];
//2.订阅信号
[subject subscribeNext:^(id x) {
NSLog(@"订阅者1:%@",x);
}];
//3.发送信号
[subject sendNext:@"1"];
通常 RACSubject使用场景:通常用来代替代理,有了它,就不必要定义代理了。
RACSubject 顺序性
我们采用两个订阅者,来看一下运行情况
- (void)RACSubject{
//1.Subject信号
RACSubject *subject = [RACSubject subject];
//2.订阅信号
[subject subscribeNext:^(id x) {
NSLog(@"订阅者1:%@",x);
}];
//3.发送信号
[subject sendNext:@"1"];
[subject subscribeNext:^(id x) {
NSLog(@"订阅者2:%@",x);
}];
[subject sendNext:@"2"];
[subject sendCompleted];
}
2020-04-15 09:06:34.976647+0800 ReadReativeCocoa[1862:222088] 订阅者1:1
2020-04-15 09:06:34.976874+0800 ReadReativeCocoa[1862:222088] 订阅者1:2
2020-04-15 09:06:34.977003+0800 ReadReativeCocoa[1862:222088] 订阅者2:2
这里面观察结果输出,我们可以不难看到第一个订阅者订阅者1可以订阅1和2两个信号发送的消息,而第二个信号只能订阅第二个信号。
继续拓展订阅信号3
- (void)RACSubject{
//1.Subject信号
RACSubject *subject = [RACSubject subject];
//2.订阅信号
[subject subscribeNext:^(id x) {
NSLog(@"订阅者1:%@",x);
}];
//3.发送信号
[subject sendNext:@"1"];
[subject subscribeNext:^(id x) {
NSLog(@"订阅者2:%@",x);
}];
[subject sendNext:@"2"];
[subject subscribeNext:^(id x) {
NSLog(@"订阅者3:%@",x);
}];
[subject sendNext:@"3"];
[subject sendCompleted];
}
2020-04-15 09:23:17.558096+0800 ReadReativeCocoa[2124:254309] 订阅者1:1
2020-04-15 09:23:17.558326+0800 ReadReativeCocoa[2124:254309] 订阅者1:2
2020-04-15 09:23:17.558475+0800 ReadReativeCocoa[2124:254309] 订阅者2:2
2020-04-15 09:23:17.558613+0800 ReadReativeCocoa[2124:254309] 订阅者1:3
2020-04-15 09:23:17.558737+0800 ReadReativeCocoa[2124:254309] 订阅者2:3
2020-04-15 09:23:17.558861+0800 ReadReativeCocoa[2124:254309] 订阅者3:3
此处我们不难看到,RACSubject 订阅者1之所以能够订阅1,2,3的信号信息,是因为先订阅信号,而订阅者2和订阅者3在上一个订阅后面,因此,只能响应后面的发送的信息。
下一篇我们继续讲解可以颠覆顺序性的RACReplaySubject类。
源码下载
源码下载: ReativeCocoa.