RACSignal的Subscription深入分析
2015-07-10 00:43
183 查看
ReactiveCocoa是一个FRP的思想在Objective-C中的实现框架,目前在美团的项目中被广泛使用。对于ReactiveCocoa的基本用法,网上有很多相关的资料,本文不再讨论。RACSignal是ReactiveCocoa中一个非常重要的概念,而本文主要关注RACSignal的实现原理。在阅读之前,你需要基本掌握RACSignal的基本用法
本文主要包含2个部分,前半部分主要分析RACSignal的subscription过程,后半部分是对前半部分的深入,在subscription过程的基础上分析ReactiveCocoa中比较难理解的两个操作:multicast && replay。
PS:为了解释清楚,我们下面只讨论next,不讨论error以及completed,这二者与next类似。本文基于ReactiveCocoa 2.x版本。
我们先刨析RACSignal的subscription过程
RACSignal的Subscription过程概括起来可以分为三个步骤:
[RACSignal createSignal]来获得signal
[signal subscribeNext:]来获得subscriber,然后进行subscription
进入didSubscribe,通过[subscriber sendNext:]来执行next block
[RACSignal createSignal]会调用子类RACDynamicSignal的createSignal来返回一个signal,并在signal中保存后面的 didSubscribe这个block
[signal subscribeNext]先会获得一个subscriber,这个subscriber中保存了nextBlock、errorBlock、completedBlock
由于这个signal其实是RACDynamicSignal类型的,这个[self subscribe]方法会调用步骤一中保存的didSubscribe,参数就是1中的subscriber
任何时候这个[subscriber sendNext:],就直接调用nextBlock
从上面的三个步骤,我们看出:
先通过createSignal和subscribeNext这两个调用,
didSubscribe block块中异步处理完毕之后,subscriber进行sendNext,
搞清楚了RAC的subscription过程,接着在此基础上我们讨论一个RACSignal中比较容易混淆的两个操作:multicast和replay。
在RACSignal+Operation.h中,连续定义了5个跟我们这个主题有关的RACSignal的操作,这几个操作的区别很细微,但用错的话很容易出问题。只有理解了原理之后,才明白它们之间的细微区别
很多时候我们意识不到需要用这些操作,这就可能因为side effects执行多次而导致程序bug
"Side effects occur for each subscription by default, but there are certain situations where side effects should only occur once – for example, a network request typically should not be repeated when a new subscriber is added."
在上面的例子中,如果我们不用RACMulticastConnection的话,那就会因为执行了两次subscription而导致发了两次网络请求。
从上面的例子中,我们可以看到对一个Signal进行multicast之后,我们是对connection.signal进行subscription而不是原来的networkRequest。这点是"side effects should only occur once"的关键,我们将在后面解释
replay是multicast的一个特殊case而已,而multicast的整个过程可以拆分成两个步骤,下面进行详细讨论
结合上面的例子来看,RACMulticastConnection的init是以networkRequest作为sourceSignal,而最终connnection.signal指的是[RACReplaySubject subject]
结合上面的RACSignal分析的Subscription过程,[self.sourceSignal subscribe:_signal]会执行self.sourceSignal的didSubscribe这个block。再结合上面的例子,也就是说会把_signal作为subscriber,发网络请求,success的时候,_signal会sendNext,这里的这个signal就是[RACReplaySubject
subject]。可以看出,一旦进入到这个didSubscribe中,后续的不管是sendNext还是subscription,都是对这个[RACReplaySubject subject]进行的,与原来的sourceSignal彻底无关了。这就解释了为什么"side effects only occur once"。
在进行multicast的步骤二之前,需要介绍一下RACSubject以及RACReplaySubject
---------------------恼人的分隔线 start------------------
"A subject can be thought of as a signal that you can manually control by sending next, completed, and error."
RACSubject的一个用法如下:
接下来分析RACSubject的原理
RACSubject中有一个subscribers数组
从subscribe:的实现可以看出,对RACSubject对象的每次subscription,都是将这个subscriber加到subscribers数组中而已
从sendNext:的实现可以看出,每次RACSubject对象sendNext,都会对其中保留的subscribers进行sendNext,如果这个subscriber是RACSignal的话,就会执行Signal的next block。
"A replay subject saves the values it is sent (up to its defined capacity) and resends those to new subscribers.",可以看出,replaySubject是可以对它send next(error,completed)的东西进行buffer的。
RACReplaySubject是继承自RACSubject的,它的内部的实现例如subscribe:、sendNext:的实现也会调用super的实现
从init中我们看出,RACReplaySubject对象持有capacity变量(用于决定valuesReceived缓存多少个sendNext:出来的value,这在区分replay和replayLast的时候特别有用)以及valuesReceived数组(用来保存sendNext:出来的value),这二者接下来会重点涉及到
从subscribe:可以看出,RACReplaySubject对象每次subscription,都会把之前valuesReceived中buffer的value重新sendNext一遍,然后调用super把当前的subscriber加入到subscribers数组中
从sendNext:可以看出,RACReplaySubject对象会buffer每次sendNext的value,然后会调用super,对subscribers中的每个subscriber,调用sendNext。buffer的数量是根据self.capacity来决定的
---------------------恼人的分隔线 end------------------
介绍完了RACReplaySubject之后,我们继续进行multicast的part 2部分。
在上面的例子中,我们对connection.signal进行了两次subscription,结合上面的RACReplaySubject的subscription的subscribe:,我们得到以下过程:
[RACReplaySubject subject]会将这两次subscription过程中的subscriber都保存在subscribers数组中
当网络请求success后,会[subscriber sendNext:response],前面已经讲过这个subscriber就是[RACReplaySubject subject],这样,就会把sendNext:的value保存在valuesReceived数组中,供后续subscription使用(不知道你是否注意到RACReplaySubject的subscribe:中有个for循环),然后对subscribers中保存的每个subscriber执行sendNext。
上面讨论的是RACReplaySubject对象先进行subscription,再进行sendNext,如果是先sendNext,再subscription呢?其实魅力就在于RACReplaySubject的subscribe:中的for循环。具体过程留作思考
在RACSignal+Operation中关于multicast && replay的,一共有5个操作:publish、multicast、replay、replayLast、replayLazily,他们之间有什么细微的差别呢?相信在我上面内容的基础上,他们之间的细微差别不难理解,这里推荐一篇帮助大家理解的blog
iOS函数响应式编程框架ReactiveCocoa概览
让我们了解下这个框架的各个类,并尝试让⼤家理解它们是如何在一起协同工作的。
⼀、所有类的关系图
上图列出了⼏乎所有的的类,其中RACSignal是重点,⽽黄⾊背景的都是私有类,可以不需要关⼼。
二、重点类解释
1, streams
streams代表任意的值,其值会随着事件发⽣变化,由RACStream类表示。值可能⻢上可用,或者
在将来某⼀段时间可用,但必须按顺序获取,也就是说,在获取到第⼀个值之前,是不可能获取到第⼆个值。
streams
是⼀个构造因果关系的结构(Monad),要了解这个概念可以看这篇⽂文章:http://blog.csdn.net/ensoo/article/details/7506872。它可以实现在基本的初始值上进行复杂的操作运算(filter,map,reducet等)。streams不会被经常使⽤用,⼤多情况下表现为signal和sequences,即Signal和Sequence是由stream继承。
2,Signals由RACSignal类表示。
Signals⼀般表示将来被传递的数据。当信号接收到数据时,值就会通过signal被发送出去,它推送数据给订阅者。用户必须订阅信号才能获取到它的值。Signals发送3种不同类型的事件给它的订阅者:1)next:next事件是stream提供了⼀个新值。⽽RACStream类的方法也只能在这个值上进⾏行操作运算。和cocoa的集合不同的是,它可以包含⼀个nil值。2)error:error事件表⽰示在信号完成之前发⽣了错误。这个事件包含了⼀个NSError类型的错误。这个值不会在RACStream类⾥存储。3)completed事件:表示信号已经成功地完成了。这个值也不会在RACStream类⾥存储。
3,Subscription(订阅者)表⽰等待或者能够等待信号发送事件的任意对象。在框架中使用RACSubscriber协议表示,也即任意实现了RACSubscriber协议的对象都可以是订阅者。
可以通过调⽤ -subscribeNext:error:completed:⽅法来创建订阅。RACStream和RACSignal类的大多操作也会⾃己创建订阅。订阅会对Signals对象引⽤计数加1,当信号发送错误或者完成事件后,会⾃动被处理,不需要⽤户关⼼内存管理。当然,⽤户也可以⼿动处理。
4, Subjects
subject由RACSubject类表⽰示,它是可以手动控制的信号。subject可以想成是signal的变体,就像NSMutableArray相对于NSArray⼀样。它们是⾮RAC的代码和RAC代码之间的桥梁,因此,⾮常有用。有些subject提供了额外的功能。特别地,RACReplaySubject⽤用于缓存事件,将来有订阅者时,可以将缓存的数据发送给订阅者,⽐如,当请求⼀个⽹络时,服务器数据已经返回,但其它的数据还没有准备好,这时,先要将网络请求结果缓存,等其它数据准备好时,再订阅,⽽不⾄于网络请求数据丢失。
5,Commands
command由RACCommand类表⽰示,它创建并订阅响应action的信号。通常command是由UI触发的,像⼀个按钮被点击时。当command被触发时,控件会⾃动被禁用。
6,Connection
connection由RACMulticastConnection类表⽰示。它是在任意数量的订阅者之间共享订阅。默认情况下,信号没有订阅时,是冷信号,也即当有订阅者被添加进来后,信号才会开始工作,也即变为热信号。这也是期望的行为,对于每个订阅,数据值都会被延迟重新计算。否则,如果信号有副作⽤用或者任务开销很⼤大时,可能就会产⽣问题。
conection通过RACSignal的publish或者multicast⽅法创建,
⼀旦连接,信号就变为热信息,订阅会保持激活状态直到所有的订阅连接被取消。
7,Sequences
sequence由RACSequence类表⽰示。sequence是⼀种集合,类似于NSArray。和数组不同,为了改进性能,在sequence⾥的值会延迟计算,也即,只有需要输出值时,结果才会被计算。sequences和cocoa里的集合⼀样,也不能包含nil值。
对于⼤多数据的集合类,RAC添加了-rac_sequence⽅法来⽣成RACSequence类进行操作。
8,Disposables
RACDisposable 类⽤于取消订阅或者清理资源。Disposables⼤多情况下⽤于取消信号的订阅 。
9,Schedulers
scheduler由RACScheduler类表示,它是信号执行任务时所在的队列(queue)或者信号执⾏完后将结果放到队列⾥执行,可以认为就是gcd⾥的queues。scheduler⽀持取消操作,⽽且它总是串行地执⾏任务。这有利于避免死锁。RACScheduler有时候也类似NSOperationQueue,但它不允许任务间相互依赖。
10,Value typesRAC提供了以下类⽤用于表⽰各种值:
1)RACTuple:
⼀个⽐较少数量值,大小固定的,可以包含nil的集合。⼀般用于表示多个streams的聚合值。
2)RACUnit:⼀个单例的空值,表示stream不包含有意义的值。
3)RACEvent:
代表任意的信号事件。
11,Asynchronous Backtraces由于基于RAC的代码经常和异步相关,因此,为了更⽅便调试,RAC支持捕获当前的asynchronousbacktraces(异步调⽤用栈)。
在OS X系统上,⽀持捕获所有代码,包括系统库。
而iOS,只⽀持RAC及⼯程内的代码。
三、工作原理
ReactiveCocoa github主页
ReactiveCocoa Documentation
ReactiveCocoa raywenderlich上的资料
本文主要包含2个部分,前半部分主要分析RACSignal的subscription过程,后半部分是对前半部分的深入,在subscription过程的基础上分析ReactiveCocoa中比较难理解的两个操作:multicast && replay。
PS:为了解释清楚,我们下面只讨论next,不讨论error以及completed,这二者与next类似。本文基于ReactiveCocoa 2.x版本。
我们先刨析RACSignal的subscription过程
RACSignal的常见用法
-(RACSignal *)signInSignal { // part 1:[RACSignal createSignal]来获得signal return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [self.signInService signInWithUsername:self.usernameTextField.text password:self.passwordTextField.text complete:^(BOOL success) { // part 3: 进入didSubscribe,通过[subscriber sendNext:]来执行next block [subscriber sendNext:@(success)]; [subscriber sendCompleted]; }]; return nil; }]; } // part 2 : [signal subscribeNext:]来获得subscriber,然后进行subscription [[self signInSignal] subscribeNext:^(id x) { NSLog(@"Sign in result: %@", x); }];
Subscription过程概括
RACSignal的Subscription过程概括起来可以分为三个步骤:[RACSignal createSignal]来获得signal
[signal subscribeNext:]来获得subscriber,然后进行subscription
进入didSubscribe,通过[subscriber sendNext:]来执行next block
步骤一:[RACSignal createSignal]来获得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:" ]; }
[RACSignal createSignal]会调用子类RACDynamicSignal的createSignal来返回一个signal,并在signal中保存后面的 didSubscribe这个block
步骤二:[signal subscribeNext:]来获得subscriber,然后进行subscription
RACSignal.m中: - ( RACDisposable *)subscribeNext:( void (^)( id x))nextBlock { RACSubscriber *o = [ RACSubscriber subscriberWithNext :nextBlock error : NULL completed : NULL ]; return [ self subscribe :o]; } RACSubscriber.m中: + ( instancetype )subscriberWithNext:( void (^)( id x))next error:( void (^)( NSError *error))error completed:( void (^)( void ))completed { RACSubscriber *subscriber = [[ self alloc ] init ]; subscriber-> _next = [next copy ]; subscriber-> _error = [error copy ]; subscriber-> _completed = [completed copy ]; return subscriber; } RACDynamicSignal.m中: - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber { 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; }
[signal subscribeNext]先会获得一个subscriber,这个subscriber中保存了nextBlock、errorBlock、completedBlock
由于这个signal其实是RACDynamicSignal类型的,这个[self subscribe]方法会调用步骤一中保存的didSubscribe,参数就是1中的subscriber
步骤三:进入didSubscribe,通过[subscriber sendNext:]来执行next block
RACSubscriber.m中: - (void)sendNext:(id)value { @synchronized (self) { void (^nextBlock)(id) = [self.next copy]; if (nextBlock == nil) return; nextBlock(value); } }
任何时候这个[subscriber sendNext:],就直接调用nextBlock
signal的subscription过程回顾
从上面的三个步骤,我们看出:先通过createSignal和subscribeNext这两个调用,
声明了流中value到来时的处理方式
didSubscribe block块中异步处理完毕之后,subscriber进行sendNext,
自动处理
搞清楚了RAC的subscription过程,接着在此基础上我们讨论一个RACSignal中比较容易混淆的两个操作:multicast和replay。
为什么要清楚这两者的原理
RACSignal+Operation.h中 - (RACMulticastConnection *)publish; - (RACMulticastConnection *)multicast:(RACSubject *)subject; - (RACSignal *)replay; - (RACSignal *)replayLast; - (RACSignal *)replayLazily;
在RACSignal+Operation.h中,连续定义了5个跟我们这个主题有关的RACSignal的操作,这几个操作的区别很细微,但用错的话很容易出问题。只有理解了原理之后,才明白它们之间的细微区别
很多时候我们意识不到需要用这些操作,这就可能因为side effects执行多次而导致程序bug
multicast && replay的应用场景
"Side effects occur for each subscription by default, but there are certain situations where side effects should only occur once – for example, a network request typically should not be repeated when a new subscriber is added."// 引用ReactiveCocoa源码的Documentation目录下的一个例子 // This signal starts a new request on each subscription. RACSignal *networkRequest = [RACSignal createSignal:^(id<RACSubscriber> subscriber) { AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id response) { [subscriber sendNext:response]; [subscriber sendCompleted]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { [subscriber sendError:error]; }]; [client enqueueHTTPRequestOperation:operation]; return [RACDisposable disposableWithBlock:^{ [operation cancel]; }]; }]; // Starts a single request, no matter how many subscriptions `connection.signal` // gets. This is equivalent to the -replay operator, or similar to // +startEagerlyWithScheduler:block:. RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]]; [connection connect]; [connection.signal subscribeNext:^(id response) { NSLog(@"subscriber one: %@", response); }]; [connection.signal subscribeNext:^(id response) { NSLog(@"subscriber two: %@", response); }];
在上面的例子中,如果我们不用RACMulticastConnection的话,那就会因为执行了两次subscription而导致发了两次网络请求。
从上面的例子中,我们可以看到对一个Signal进行multicast之后,我们是对connection.signal进行subscription而不是原来的networkRequest。这点是"side effects should only occur once"的关键,我们将在后面解释
multicast原理分析
replay是multicast的一个特殊case而已,而multicast的整个过程可以拆分成两个步骤,下面进行详细讨论
multicast的机制Part 1:
RACMulticastConnection.m中: - (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject { NSCParameterAssert(source != nil); NSCParameterAssert(subject != nil); self = [super init]; if (self == nil) return nil; _sourceSignal = source; _serialDisposable = [[RACSerialDisposable alloc] init]; _signal = subject; return self; }
结合上面的例子来看,RACMulticastConnection的init是以networkRequest作为sourceSignal,而最终connnection.signal指的是[RACReplaySubject subject]
RACMulticastConnection.m中: - (RACDisposable *)connect { BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected); if (shouldConnect) { self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal]; } return self.serialDisposable; }
结合上面的RACSignal分析的Subscription过程,[self.sourceSignal subscribe:_signal]会执行self.sourceSignal的didSubscribe这个block。再结合上面的例子,也就是说会把_signal作为subscriber,发网络请求,success的时候,_signal会sendNext,这里的这个signal就是[RACReplaySubject
subject]。可以看出,一旦进入到这个didSubscribe中,后续的不管是sendNext还是subscription,都是对这个[RACReplaySubject subject]进行的,与原来的sourceSignal彻底无关了。这就解释了为什么"side effects only occur once"。
multicast的机制Part 2:
在进行multicast的步骤二之前,需要介绍一下RACSubject以及RACReplaySubject---------------------恼人的分隔线 start------------------
RACSubject
"A subject can be thought of as a signal that you can manually control by sending next, completed, and error."RACSubject的一个用法如下:
RACSubject *letters = [RACSubject subject]; // Outputs: A B [letters subscribeNext:^(id x) { NSLog(@"%@ ", x); }]; [letters sendNext:@"A"]; [letters sendNext:@"B"];
接下来分析RACSubject的原理
RACSubject.m中: - (id)init { self = [super init]; if (self == nil) return nil; _disposable = [RACCompoundDisposable compoundDisposable]; _subscribers = [[NSMutableArray alloc] initWithCapacity:1]; return self; }
RACSubject中有一个subscribers数组
RACSubject.m中: - (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]; } }]; }
从subscribe:的实现可以看出,对RACSubject对象的每次subscription,都是将这个subscriber加到subscribers数组中而已
RACSubject.m中: - (void)sendNext:(id)value { [self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) { [subscriber sendNext:value]; }]; }
从sendNext:的实现可以看出,每次RACSubject对象sendNext,都会对其中保留的subscribers进行sendNext,如果这个subscriber是RACSignal的话,就会执行Signal的next block。
RACReplaySubject
"A replay subject saves the values it is sent (up to its defined capacity) and resends those to new subscribers.",可以看出,replaySubject是可以对它send next(error,completed)的东西进行buffer的。RACReplaySubject是继承自RACSubject的,它的内部的实现例如subscribe:、sendNext:的实现也会调用super的实现
RACReplaySubject.m中: - (instancetype)initWithCapacity:(NSUInteger)capacity { self = [super init]; if (self == nil) return nil; _capacity = capacity; _valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]); return self; }
从init中我们看出,RACReplaySubject对象持有capacity变量(用于决定valuesReceived缓存多少个sendNext:出来的value,这在区分replay和replayLast的时候特别有用)以及valuesReceived数组(用来保存sendNext:出来的value),这二者接下来会重点涉及到
RACReplaySubject.m中: - (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; }
从subscribe:可以看出,RACReplaySubject对象每次subscription,都会把之前valuesReceived中buffer的value重新sendNext一遍,然后调用super把当前的subscriber加入到subscribers数组中
RACReplaySubject.m中: - (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)]; } } }
从sendNext:可以看出,RACReplaySubject对象会buffer每次sendNext的value,然后会调用super,对subscribers中的每个subscriber,调用sendNext。buffer的数量是根据self.capacity来决定的
---------------------恼人的分隔线 end------------------
介绍完了RACReplaySubject之后,我们继续进行multicast的part 2部分。
在上面的例子中,我们对connection.signal进行了两次subscription,结合上面的RACReplaySubject的subscription的subscribe:,我们得到以下过程:
[RACReplaySubject subject]会将这两次subscription过程中的subscriber都保存在subscribers数组中
当网络请求success后,会[subscriber sendNext:response],前面已经讲过这个subscriber就是[RACReplaySubject subject],这样,就会把sendNext:的value保存在valuesReceived数组中,供后续subscription使用(不知道你是否注意到RACReplaySubject的subscribe:中有个for循环),然后对subscribers中保存的每个subscriber执行sendNext。
后续思考
上面讨论的是RACReplaySubject对象先进行subscription,再进行sendNext,如果是先sendNext,再subscription呢?其实魅力就在于RACReplaySubject的subscribe:中的for循环。具体过程留作思考在RACSignal+Operation中关于multicast && replay的,一共有5个操作:publish、multicast、replay、replayLast、replayLazily,他们之间有什么细微的差别呢?相信在我上面内容的基础上,他们之间的细微差别不难理解,这里推荐一篇帮助大家理解的blog
iOS函数响应式编程框架ReactiveCocoa概览
让我们了解下这个框架的各个类,并尝试让⼤家理解它们是如何在一起协同工作的。
⼀、所有类的关系图
上图列出了⼏乎所有的的类,其中RACSignal是重点,⽽黄⾊背景的都是私有类,可以不需要关⼼。
二、重点类解释
1, streams
streams代表任意的值,其值会随着事件发⽣变化,由RACStream类表示。值可能⻢上可用,或者
在将来某⼀段时间可用,但必须按顺序获取,也就是说,在获取到第⼀个值之前,是不可能获取到第⼆个值。
streams
是⼀个构造因果关系的结构(Monad),要了解这个概念可以看这篇⽂文章:http://blog.csdn.net/ensoo/article/details/7506872。它可以实现在基本的初始值上进行复杂的操作运算(filter,map,reducet等)。streams不会被经常使⽤用,⼤多情况下表现为signal和sequences,即Signal和Sequence是由stream继承。
2,Signals由RACSignal类表示。
Signals⼀般表示将来被传递的数据。当信号接收到数据时,值就会通过signal被发送出去,它推送数据给订阅者。用户必须订阅信号才能获取到它的值。Signals发送3种不同类型的事件给它的订阅者:1)next:next事件是stream提供了⼀个新值。⽽RACStream类的方法也只能在这个值上进⾏行操作运算。和cocoa的集合不同的是,它可以包含⼀个nil值。2)error:error事件表⽰示在信号完成之前发⽣了错误。这个事件包含了⼀个NSError类型的错误。这个值不会在RACStream类⾥存储。3)completed事件:表示信号已经成功地完成了。这个值也不会在RACStream类⾥存储。
3,Subscription(订阅者)表⽰等待或者能够等待信号发送事件的任意对象。在框架中使用RACSubscriber协议表示,也即任意实现了RACSubscriber协议的对象都可以是订阅者。
可以通过调⽤ -subscribeNext:error:completed:⽅法来创建订阅。RACStream和RACSignal类的大多操作也会⾃己创建订阅。订阅会对Signals对象引⽤计数加1,当信号发送错误或者完成事件后,会⾃动被处理,不需要⽤户关⼼内存管理。当然,⽤户也可以⼿动处理。
4, Subjects
subject由RACSubject类表⽰示,它是可以手动控制的信号。subject可以想成是signal的变体,就像NSMutableArray相对于NSArray⼀样。它们是⾮RAC的代码和RAC代码之间的桥梁,因此,⾮常有用。有些subject提供了额外的功能。特别地,RACReplaySubject⽤用于缓存事件,将来有订阅者时,可以将缓存的数据发送给订阅者,⽐如,当请求⼀个⽹络时,服务器数据已经返回,但其它的数据还没有准备好,这时,先要将网络请求结果缓存,等其它数据准备好时,再订阅,⽽不⾄于网络请求数据丢失。
5,Commands
command由RACCommand类表⽰示,它创建并订阅响应action的信号。通常command是由UI触发的,像⼀个按钮被点击时。当command被触发时,控件会⾃动被禁用。
6,Connection
connection由RACMulticastConnection类表⽰示。它是在任意数量的订阅者之间共享订阅。默认情况下,信号没有订阅时,是冷信号,也即当有订阅者被添加进来后,信号才会开始工作,也即变为热信号。这也是期望的行为,对于每个订阅,数据值都会被延迟重新计算。否则,如果信号有副作⽤用或者任务开销很⼤大时,可能就会产⽣问题。
conection通过RACSignal的publish或者multicast⽅法创建,
⼀旦连接,信号就变为热信息,订阅会保持激活状态直到所有的订阅连接被取消。
7,Sequences
sequence由RACSequence类表⽰示。sequence是⼀种集合,类似于NSArray。和数组不同,为了改进性能,在sequence⾥的值会延迟计算,也即,只有需要输出值时,结果才会被计算。sequences和cocoa里的集合⼀样,也不能包含nil值。
对于⼤多数据的集合类,RAC添加了-rac_sequence⽅法来⽣成RACSequence类进行操作。
8,Disposables
RACDisposable 类⽤于取消订阅或者清理资源。Disposables⼤多情况下⽤于取消信号的订阅 。
9,Schedulers
scheduler由RACScheduler类表示,它是信号执行任务时所在的队列(queue)或者信号执⾏完后将结果放到队列⾥执行,可以认为就是gcd⾥的queues。scheduler⽀持取消操作,⽽且它总是串行地执⾏任务。这有利于避免死锁。RACScheduler有时候也类似NSOperationQueue,但它不允许任务间相互依赖。
10,Value typesRAC提供了以下类⽤用于表⽰各种值:
1)RACTuple:
⼀个⽐较少数量值,大小固定的,可以包含nil的集合。⼀般用于表示多个streams的聚合值。
2)RACUnit:⼀个单例的空值,表示stream不包含有意义的值。
3)RACEvent:
代表任意的信号事件。
11,Asynchronous Backtraces由于基于RAC的代码经常和异步相关,因此,为了更⽅便调试,RAC支持捕获当前的asynchronousbacktraces(异步调⽤用栈)。
在OS X系统上,⽀持捕获所有代码,包括系统库。
而iOS,只⽀持RAC及⼯程内的代码。
三、工作原理
参考资料
ReactiveCocoa github主页ReactiveCocoa Documentation
ReactiveCocoa raywenderlich上的资料
相关文章推荐
- Automatic Preferred Max Layout Width is not available on iOS versions prior to 8.0
- python_技巧
- 蚁视头盔上手简评
- iOS中UILabel 多行显示
- Flex-box 学习
- Comparing replay, replayLast, and replayLazily
- Microsoft Sync Framework下的快速开发同步程序
- 20150709
- java九大内置对象
- USB Packet Types
- ObjectStreamDemo
- SSH使用密钥登录并禁止口令登录实践
- Android.mk 详解剖析
- CSS3 小技巧/注意事项
- 14_Android中Service的使用,关于广播接收者的说明
- python os.exec*()家族函数的用法
- 14_Android中Service的使用,关于广播接收者的说明
- Qt中QStack简单应用
- unix命令
- servlet知识点