ReactiveCocoa 代码阅读笔记 (2) Signal的实现机制
上篇讲了RAC和RACObserve两个宏的实现机制,但都是把RACSignal当作一个黑盒来理解。本篇详细讲解Signal的内部机制。
创建Signal
Signal有很多种创建方式,例如[UITextView rac_textSignal]是用signalForSelector:的方式。这里我们先看看自定义创建的Signal:
// RACSignal.m+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe { return [RACDynamicSignal createSignal:didSubscribe];}// RACDynamicSignal.m+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe { RACDynamicSignal *signal = [[self alloc] init]; signal->_didSubscribe = [didSubscribe copy]; return [signal setNameWithFormat:@"+createSignal:"];}createSignal方法接收一个block, 把这个block存在_didSubscribe属性中。顾名思义,这个block会在subscribe的时候被调用。
subscribe一个signal
下面看最常用的subscribeNext的定义
// RACSignal.m- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock { RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock]; return [self subscribe:o];}subscribeNext会创建一个RACSubscriber对象, 它保存了nextBlock, errorBlock和completedBlock,这些block将在sendNext, sendError和sendCompleted时调用:
// RACSubscriber.m- (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(); }}可以看到, sendNext是线程安全的,并且nextBlock是和sendNext在同一个线程上执行的。
我们回到RACSignal, subscribeNext会调用subscribe:
// RACDynamicSignal.m- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber { NSCParameterAssert(subscriber != nil); RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable]; subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable]; if (self.didSubscribe != NULL) { RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ RACDisposable *innerDisposable = self.didSubscribe(subscriber); [disposable addDisposable:innerDisposable]; }]; [disposable addDisposable:schedulingDisposable]; } return disposable;}RACPassthroughSubscriber可以看作和subscriber功能一样,这里不细讲,所以subscribe:就是调用了RACScheduler.subscriptionScheduler schedule: 并传入一个block, 在这个block中调用了didSubscribe,也就是createSignal中的block。所以我们可以得到一个结论:对于createSignal创建的信号,每subscribe一次,其block就被调用一次。
RACScheduler.subscriptionScheduler返回一个单例的RACSubscriptionScheduler对象, 其schedule方法:
// RACSubscriptionScheduler.m- (RACDisposable *)schedule:(void (^)(void))block { NSCParameterAssert(block != NULL); if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block]; block(); return nil;}这个方法会找RACScheduler.currentScheduler, 这个方法在主线程上或者在schedule内部(schedule中的schedule)会返回非nil, 此时直接调用block; 如果不在主线程上,最外层schedule会使用backgroundScheduler,也就是在一个background queue上执行block.
因此, 如果createSignal在主线程上执行,(之后调用subscribe时)signal的didSubscribe(也就是createSignal传入的block)也会在主线程上执行。
一般自定义的signal会在didSubscribe这个block中(或者其后续)调用[subscriber sendNext]等方法,这样subscribeNext:中传入的block就会执行。应该注意到的是subscirbeNext:中传入的block会和sendNext在同一个线程上执行。
rac_signalForSelector:
rac_signalForSelector: 这个方法很重要,它为各种UIView的delegate方法封装提供了基础。这个方法产生一个signal, 这个signal会在receiver执行某个selector时发送函数调用的参数。以UITextView为例:
// NSObject+RACSelectorSignal- (RACSignal *)rac_signalForSelector:(SEL)selector;// UITextView+RACSignalSupport.m- (RACSignal *)rac_textSignal { @weakify(self); RACSignal *signal = [[[[[RACSignal defer:^{ @strongify(self); return [RACSignal return:RACTuplePack(self)]; }] concat:[self.rac_delegateProxy signalForSelector:@selector(textViewDidChange:)]] reduceEach:^(UITextView *x) { return x.text; }] takeUntil:self.rac_willDeallocSignal] setNameWithFormat:@"%@ -rac_textSignal", self.rac_description]; RACUseDelegateProxy(self); return signal;}这里会用一个self.rac_delegateProxy作为TextView的proxy,调用[self.rac_delegateProxy signalForSelector:@selector(textViewDidChange:)] 也就是产生了一个信号, 当[delegate textViewDidChange:]调用时触发。
rac_signalForSelector中创建了一个subject(一个可以手动控制进行sendNext的信号), 做了很复杂的swizzling, 其中和发送信号相关的是RACSwizzleForwardInvocation, 这个方法改写了原本的forwardInvocation. 然后将自己的对应selector的方法替换成forwardInvocation,这个forwardInvocation里面会调用[subject sendNext:]发送信号给subscriber.
一句话概括:rac_textSignal会”劫持” UITextView.delegate的textViewDidChange:方法,然后在这个方法调用时,把这个方法传入的参数发送给subscriber。这个劫持过程的实现是由[NSObject rac_signalForSelector:]方法完成的。