ReactiveCocoa源码精讲(二) -- RACSubject

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.

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值