Subscriber
到了最后一個(gè)部分了--訂閱者. 其實(shí)在Signal篇我們已經(jīng)接觸過了一些Subscriber, 畢竟Subject也是一個(gè)Subscriber.
而與RACSignal和RACDisposable不同的是, 與Subscriber相關(guān)的其它類不是用的繼承的形式, 而是組合(也就是其它類持有了一個(gè)RACSubscriber實(shí)例, 例如RACPassthroughSubscriber).
RACSubscriber
RACSubcriber是一般訂閱者的類, 整體而言代碼也是比較簡潔的, 首先要說明的是RACSubscriber有一個(gè)protocol的聲明, 也有一個(gè)類的聲明, 對于一個(gè)協(xié)議來說, 它Required了以下幾個(gè)方法:
- (void)sendNext:(id)value;
- (void)sendError:(NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
前面3個(gè)基本都見過了, 最后一個(gè)在Subject也見過一次. RACSubscriber類有個(gè)類方法返回實(shí)例, 但是頭文件標(biāo)注為私有的, 也就是說一般我們使用的時(shí)候不要關(guān)注它:
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed;
再看看私有變量:
@interface RACSubscriber ()
@property (nonatomic, copy) void (^next)(id value);
@property (nonatomic, copy) void (^error)(NSError *error);
@property (nonatomic, copy) void (^completed)(void);
// 因?yàn)閰f(xié)議中有了addDisposable, 所以理所當(dāng)然要有個(gè)RACCompoundDisposable實(shí)例
@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;
@end
再看看幾個(gè)重要方法:
- (id)init {
self = [super init];
if (self == nil) return nil;
@unsafeify(self);
// 首先要添加一個(gè)釋放自己資源的disposable
RACDisposable *selfDisposable = [RACDisposable disposableWithBlock:^{
@strongify(self);
@synchronized (self) {
self.next = nil;
self.error = nil;
self.completed = nil;
}
}];
_disposable = [RACCompoundDisposable compoundDisposable];
[_disposable addDisposable:selfDisposable];
return self;
}
- (void)dealloc {
[self.disposable dispose];
}
值得一提的是, 雖然與Subscriber與Subject的作者都是同一人, 但是2者創(chuàng)建的時(shí)間卻是一個(gè)1月一個(gè)9月, 所以在寫的時(shí)候還是會有所區(qū)別, 特別是@unsafeify和@weakify, 之前看到一篇博文應(yīng)該是王巍寫的, 專門講RAC里面宏的精要, 還是很值得一看的.
再看看3個(gè)發(fā)送事件的方法:
- (void)sendNext:(id)value {
@synchronized (self) {
void (^nextBlock)(id) = [self.next copy];
if (nextBlock == nil) return;
nextBlock(value);
}
}
- (void)sendError:(NSError *)e {
@synchronized (self) {
void (^errorBlock)(NSError *) = [self.error copy];
[self.disposable dispose];
if (errorBlock == nil) return;
errorBlock(e);
}
}
- (void)sendCompleted {
@synchronized (self) {
void (^completedBlock)(void) = [self.completed copy];
[self.disposable dispose];
if (completedBlock == nil) return;
completedBlock();
}
}
可以看到前期作者在寫的時(shí)候非常謹(jǐn)小慎微, 各種同步加持, 還用臨時(shí)變量來保證線程安全, 到了Subject的時(shí)候就會更加"隨意"一些.
同樣的, 最后這個(gè)didSubscribeWithDisposable也是如此, 這里以這個(gè)方法來看看Subject里面和Subscriber里面的實(shí)現(xiàn)差異
// RACSubscriber.m
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)otherDisposable {
if (otherDisposable.disposed) return;
RACCompoundDisposable *selfDisposable = self.disposable;
[selfDisposable addDisposable:otherDisposable];
@unsafeify(otherDisposable);
[otherDisposable addDisposable:[RACDisposable disposableWithBlock:^{
@strongify(otherDisposable);
[selfDisposable removeDisposable:otherDisposable];
}]];
}
// RACSubject.m
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)d {
if (d.disposed) return;
[self.disposable addDisposable:d];
@weakify(self, d);
[d addDisposable:[RACDisposable disposableWithBlock:^{
@strongify(self, d);
[self.disposable removeDisposable:d];
}]];
}
發(fā)現(xiàn)去掉臨時(shí)變量之后, 二者基本上是一致的.
RACPassthroughSubscriber
在我們訂閱的時(shí)候, 實(shí)際創(chuàng)建的實(shí)例是RACPassthroughSubscriber, 但是一般我們不直接創(chuàng)建它, 都是RAC本身的實(shí)現(xiàn).
先看看頭文件:
@interface RACPassthroughSubscriber : NSObject <RACSubscriber>
- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable;
實(shí)現(xiàn)了RACSubscriber協(xié)議, 并且signal, subcriber, disposable一應(yīng)俱全, 看起來這個(gè)類會維系好三者之間的關(guān)系.
看實(shí)現(xiàn)文件時(shí), 發(fā)現(xiàn)里面又來了一些 DTrace 的東西, 這個(gè)東西在 objc.io 里面有過介紹, Xcode 的Instruments就是基于此實(shí)現(xiàn), 有興趣的可以去看看. 從這個(gè)側(cè)面也應(yīng)該看的出來, RAC 團(tuán)隊(duì)對框架的性能是有過比較深度的評測的, 因此還是值得信賴的.
同時(shí)需要提一下的是, DTrace 的屬性沒有標(biāo)注為weak 而是unsafe_unretained, 眾所周知, weak 的屬性會在指向的對象被釋放后自動(dòng)置為nil, 因此要實(shí)現(xiàn)這一點(diǎn), 肯定就會在目標(biāo)對象上有存儲 weak 引用的列表, 據(jù)測為了保證 weak 引用, 中間產(chǎn)生的開銷還是不小的, 所以 RAC 會只在 DTrace 中用的屬性用unsafe_unretained, 以免造成不必要的開銷. 當(dāng)然, 使用了unsafe_unretained的屬性, 就不能輕易去 get 和 set 否則很容易造成野指針 crash.
實(shí)例初始化代碼:
- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
NSCParameterAssert(subscriber != nil);
self = [super init];
if (self == nil) return nil;
_innerSubscriber = subscriber;
_signal = signal;
_disposable = disposable;
// 添加傳入的 disposable, 在調(diào)用 dispose 的時(shí)候一并清除掉
[self.innerSubscriber didSubscribeWithDisposable:self.disposable];
return self;
}
RACSubscriber協(xié)議的其它方法就基本上沒有太多可講的了, 與上面的基本上是大同小異. 那么這里會引出一個(gè)問題, 為什么不直接用 RACSubscriber, 而要再引入一個(gè) passThrough 版本呢?
根據(jù)RACPassthroughSubscriber頭文件中對這個(gè)類的描述:
A private subscriber that passes through all events to another subscriber
while not disposed.
大意是說: 在未被清除的情況下, 把所有事件都傳遞到另一個(gè)訂閱者的私有訂閱者.
前提很好理解, 清除之后自然沒有必要再傳遞, 另一個(gè)訂閱者自然也就是傳入進(jìn)來的這個(gè)訂閱者, 也就是實(shí)際上需要發(fā)送時(shí)間的 RACSubscriber. 然而這段話并沒有解釋出來為什么需要這么樣個(gè)私有訂閱者呢, 從代碼角度上來看也就是加了 DTrace 的功能而已, 并沒有什么特殊的管理三者關(guān)系的代碼存在.
目前除了 DTrace 的探針之外, 我個(gè)人找不到很好的解釋, 發(fā)送郵件給作者想要問這個(gè)問題也暫時(shí)沒有得到回復(fù), 如果有回復(fù)我會更新上來. 除此之外, 我還特地把RACDynamicSignal里面的subscribe中把普通 RACSubscriber 變?yōu)镽ACPassThroughSubscriber 的代碼給注釋掉:
// subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
發(fā)現(xiàn)對我的 demo 應(yīng)用并沒有任何干擾, 因此也算一個(gè)佐證證明RACPassThroughSubscriber的作用的猜想了.