iOS:RAC之Subject父子
前一篇RacSignal:http://www.jianshu.com/p/68598c0f5aee
对于想学习一些基础的童鞋来说,你要先看看上篇文章和下篇文章。上篇说到了RacSignal是信号的源头,一个完美的基类,在上篇文章中说道创建信号的三个要素和一系列的源码解析,我们看到了它内部复杂的回掉,那么对于RacSignal每次初始化都要创建的block,那么我们如何将发送信号和信号的初始化完美的分割开呢?下边Rac引入了RacSubject,RacReplaySubject父子,还是老规矩,先说下具体的业务实现,然后源码分析,最后伪代码搞起。
使用
- RacSubject
RacSubject的使用也是分成了三部曲,并且他们的顺序一定要掌握好!
//创建
RACSubject *subject1 = [RACSubject subject];
//订阅
[subject1 subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
//发送
[subject1 sendNext:@"1"];
RACSubject的确帮助了我们完美的分割开了初始化和回掉的过程,可以在多个层次中找到你想要的subject然后产生回掉。但是他有一个问题,假如我先发信号在订阅呢?手快的人就会发现订阅时是找不到发送的信号的,所以加入了RacReplaySubject
- RacReplaySubject
说到RacReplaySubject类,他只是对于RACSubject的封装多了一个功能,就是有了一个值的数组,后边我们会说到的,直接简单的应用代码。
RACReplaySubject *subjectReplay = [RACReplaySubject subject];
[subjectReplay sendNext:@"2"];
[subjectReplay subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
这里就不多解释了,这个地方是能拿到那个值的。
源码分析
其实相对于Signal的源码,subject只是绕了一小下下。
首先是
- RacSubject
1.创建过程
这里多了一个只读数组,用带存储订阅者。后边就知道怎么用了。
+ (instancetype)subject {
return [[self alloc] init];
}
- (id)init {
self = [super init];
if (self == nil) return nil;
_disposable = [RACCompoundDisposable compoundDisposable];
_subscribers = [[NSMutableArray alloc] initWithCapacity:1];
return self;
}
2.订阅信号
订阅信号与signal一致,创建一个订阅者,传subscribe:方法但是不一样的是subject没有拿到订阅者直接回掉,而是将这些订阅者传入一个subscribers数组中,这也是后续热信号和冷信号实质区别。
- (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 addObject:subscriber];
}
return [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];
}
}];
}
3.发送信号
每发送一个信号,就会遍历数组然后回掉,注意:signal是在订阅时回掉,而subject是在发送时触发回掉
(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];
}];
}
到此subject自己在纸上也可以看到了大致的信号传递原型。
- RacReplaySubject
其实RacReplaySubject是subject的子类初始化是大致相同,下面重点说下发送和订阅
1.发送信号
这里作者引入了RACTupleNil这个元组类对象,代替了oc中的nil防止程序崩溃,并且将buffer的value传递给了valuesReceived数组。并调用subject的发送方法将value传给了现阶段所有的订阅者。
- (void)sendNext:(id)value {
@synchronized (self) {
[self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];
[super sendNext:value];
if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {
[self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];
}
}
}
2.订阅信号
在每次订阅的时候,我们拿到的订阅者事先回掉之前replaysubject的所有value,然后将这个订阅者加入到我们的订阅者数组中,完成了value的缓存。
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
@synchronized (self) {
for (id value in self.valuesReceived) {
if (compoundDisposable.disposed) return;
[subscriber sendNext:(value == RACTupleNil.tupleNil ? nil : value)];
}
if (compoundDisposable.disposed) return;
if (self.hasCompleted) {
[subscriber sendCompleted];
} else if (self.hasError) {
[subscriber sendError:self.error];
} else {
RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
[compoundDisposable addDisposable:subscriptionDisposable];
}
}
}];
[compoundDisposable addDisposable:schedulingDisposable];
return compoundDisposable;
}
最后
rac中基础信号已经了解将近一半了但是有一个问题,作者也表明了就是signal是有局限性的,他的实际触发时机是在订阅的时候,那么我的每一个订阅者都会回掉创建时候的block,这样会不会有弊端?所以rac的作者也介绍了冷热信号的概念。
更新
一个知识点:subject问什么可以发送信号,源于他是遵守<RACSubscriber>协议,所以subject可以作为信号可以作为订阅者来使用。后面会用到这个知识点
最后说一句
关于subject父子的伪代码已经传到了,希望对于学习rac的人有帮助。git:https://github.com/Heqiao1025/RacSubjectDemo