racsignal和racracbehaviorsubjectt的区别

君,已阅读到文档的结尾了呢~~
RACSignal 冷信号和热信号底层实现分析
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
RACSignal 冷信号和热信号底层实现分析
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口iOS开发RAC学习笔记(一)RACSignal - 简书
iOS开发RAC学习笔记(一)RACSignal
StudyForRAC
If you have some questions, please tell me.
My email address is fanyang_.
RAC是一个非常强大的框架我们将会从以下方面去介绍它
RACSubject
RACSequence
RACMulticastConnection
RACCommand
项目文件夹介绍
Lianxi 文件夹大致讲述了RAC框架一些简单的使用例子
the basis of RACSignal 文件夹主要讲述了RACSignal这个类该如何去使用
the basis of RACSubject 文件夹主要讲述了RACSubject与RACSignal的小区别,以及RACSubject如何作为代理去使用
the basis of RACCommand 文件夹主要讲的是RACCommand如何使用,并监听完成情况
the skills of RAC 文件夹主要讲的是RAC的使用技巧,主要包括UI控件addTarget的替代,代替代理,代替通知,代替KVO,监听事件,定时器
Signal processing 文件夹主要讲的是信号的组合和处理
我们为什么要学习RAC?
RAC是github团队开发的一套超重量级开源框架
目的在于事件的监听,RAC几乎接管了Apple所有的事件机制,主要有以下六大点:
网络异步回调
block不能列入其中的原因很简单.block是提前准备好的代码,传递给接收方,至于什么时候执行接收方并不知道
RAC学习起来的特点
学习起来比较难
团队开发的时候需要谨慎使用
团队代码需要不断的评审,保证团队中所有人代码的风格一致!避免阅读代码的困难
RAC框架如何pod导入项目
首先如果你不限制版本请在podfile里把use_frameworks!的注释取消掉,即把#删掉即可,因为RAC框架是支持Swift的
再然后可以添加上 pod 'ReactiveCocoa', '~& 2.5'
pod install即可
使用时只要将 'ReactiveCocoa.h' 'NSObject+RACKVOWrapper.h' 'RACReturnSignal.h' 这三个文件导入就可以了
注意:若出现大量报错的话,请将Xcode升级至7.3以上
RAC框架的版本问题
4.0版本支持Swift2.0
3.0版本支持Swift1.2
2.5版本不支持Swift(Xcode7升级到Xcode8后由于swift版本变化有两种办法,一种是使用RAC2.5版本,一种就是在pod-&target兼容swift选项)
若导入框架后出现报错注意排查
首先在viewModel内创建信号,外界在调用读取信息方法时,向外界返回一个信号
-(RACSignal *)loadInfo{
return [RACSignal createSignal:^RACDisposable *(id&RACSubscriber& subscriber) {
BOOL isError = NO;
if (isError) {
//发送错误信息
[subscriber sendError:[NSError errorWithDomain:@"/SkyHarute" code:2333 userInfo:@{@"errorMessage":@"异常错误"}]];
//创建信息(只需要知道是给_dataArray赋值就可以)
[self creatInfo];
//若没有错误发送正确信息,并将数组送出
[subscriber sendNext:_dataArray];
//正确信息发送完毕后发送完成信号,若信息为错误信息则不发送完成信号
[subscriber sendCompleted];
在控制器调用该方法读取信息获取到当前信号并订阅
//这是signal对象方法中能把三种情况全部列举出来的对象方法,根据需求决定,一般使用最简单的就好
[[viewModel loadInfo] subscribeNext:^(id x) {
//接收到正常发送信号,并打印信号传过来的信息
NSLog(@"%@",x);
} error:^(NSError *error) {
//接收到错误信号,并打印出错误信息
NSLog(@"%@",error);
} completed:^{
//接收到完成信号,并打印出完成信息,若为错误信号则不打印
NSLog(@"完成");
信号的三种对象方法sendNext,sendError,sendCompleted分别对应订阅者的next,error,completed三种情况,我们只要监听订阅者的三个代码块,并写上相应的代码,就可以实现在不同的代码块获得自己想要的东西.
RAC在使用的时候由于系统提供的信号是始终存在的,所以在block中使用属性或者成员变量几乎都会涉及到一个循环引用的问题,有两种方法可以解决,使用weakself解决或者RAC提供的weak-strong dance.用法也比较简单:在 block 的外部使用 @weakify(self),在 block 的内部使用 @strongify(self),具体的方法会在demo或下文中看到
列举一些RAC常用的事件处理,这里教大家一个技巧,通过查看RAC框架中UI控件的分类便可以得知
@weakify(self);
[[self.btn_event rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
@strongify(self);
NSLog(@"%@",self.dataArray.firstObject);
textField输入内容的实时监听
[[self.tf_name rac_textSignal] subscribeNext:^(id x) {
NSLog(@"%@",x);
组合信号的使用,我们想将两个信号整合成一个信号的话这样做就可以了,这样就避免了同时订阅两个信号的苦恼
//信号组合获取,注意将id类型改为RACTuple
[[RACSignal combineLatest:@[self.tf_name.rac_textSignal,self.tf_age.rac_textSignal]] subscribeNext:^(RACTuple *x) {
NSString * name = x.
NSString * age = x.
NSLog(@"name:%@,age:%@",name,age);
信号组合时reduce的使用
//根据textfield内容决定按钮是否可以点击
// reduce 中,可以通过接收的参数进行计算,并且返回需要的数值!
[[RACSignal combineLatest:@[self.tf_name.rac_textSignal,self.tf_age.rac_textSignal] reduce:^id(NSString * name , NSString * age){
return @(name.length&0&&age.length&0);
}] subscribeNext:^(id x) {
@strongify(self);
self.btn_event.enabled = [x boolValue];
一般双向绑定是指UI控件和模型互相绑定的,一般是在在改变一个值的情况下另外一个对象也会改变,类似KVO,但KVO写的时候很多观察属性写在一个方法里对代码的可阅读性并不是很好
这里为了更好的体现出效果所以采用了textfield绑定到模型,模型绑定到label的做法,比较好理解,这样在textfiled输入文字便能够实时改变模型值,而模型值一旦改变,label的text内容也会随之改变.
直接双向绑定
RACChannelTo(self.lb_name,text) = RACChannelTo(model, name);
UI绑定模型
PersonModel * model = [[PersonModel alloc]init];
model = self.dataArray.firstO
RAC(self.lb_name,text)=RACObserve(model, name);
//这里不能使用基本数据类型,RAC中传递的都是id类型,使用基本类型会崩溃,所以使用map方法对返回值进行了更替
RAC(self.lb_age,text)=[RACObserve(model, age) map:^id(id value) {
return [value description];
[[RACSignal combineLatest:@[self.tf_name.rac_textSignal,self.tf_age.rac_textSignal]] subscribeNext:^(RACTuple * x) {
model.name = x.
model.age = [x.second intValue];
RACSubject
一个想搞音视频不混的小伙子ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,它受
的启发,是
过程中的一个副产品,它提供了一系列用来组合和转换值流的 API 。
大神是这样评价 ReactiveCocoa 的:
Breaking from a tradition of covering Apple APIs exclusively, this edition of NSHipster will look at an open source project that exemplifies this brave new era for Objective-C.
他认为 ReactiveCocoa 打破了苹果 API 排他性的束缚,勇敢地开创了 Objective-C 的新纪元,具有划时代的意义。不得不说,这对于一个第三方框架来说,已经是非常高的评价了。
关于 ReactiveCocoa 的版本演进历程,简单介绍如下:
&=v2.5 :Objective-C ;
v3.x :Swift 1.2 ;
v4.x :Swift 2.x 。
注:本文所介绍的均为 ReactiveCocoa v2.5 版本中的内容,这是 Objective-C 最新的稳定版本。另外,本文的目录结构如下
● RACStream
● RACSignal
● RACSubject
● RACSequence
● RACSubscriber
● RACMulticastConnection
● RACScheduler
● RACDisposable
○ 参考链接
ReactiveCocoa 是一个非常复杂的框架,在正式开始介绍它的核心组件前,我们先来看看它的类图,以便从宏观上了解它的层次结构:
从上面的类图中,我们可以看出,ReactiveCocoa 主要由以下四大核心组件构成:
● 信号源:RACStream 及其子类;
● 订阅者:RACSubscriber 的实现类及其子类;
● 调度器:RACScheduler 及其子类;
● 清洁工:RACDisposable 及其子类。
其中,信号源又是最核心的部分,其他组件都是围绕它运作的。
对于一个应用来说,绝大部分的时间都是在等待某些事件的发生或响应某些状态的变化,比如用户的触摸事件、应用进入后台、网络请求成功刷新界面等等,而维护这些状态的变化,常常会使代码变得非常复杂,难以扩展。而 ReactiveCocoa 给出了一种非常好的解决方案,它使用信号来代表这些异步事件,提供了一种统一的方式来处理所有异步的行为,包括代理方法、block 回调、target-action 机制、通知、KVO 等:
rac_signalForSelector:@selector(webViewDidStartLoad:)
fromProtocol:@protocol(UIWebViewDelegate)]
subscribeNext:^(id x) {
[[self.avatarButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(UIButton *avatarButton) {
[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:kReachabilityChangedNotification object:nil]
subscribeNext:^(NSNotification *notification) {
[RACObserve(self, username) subscribeNext:^(NSString *username) {
然而,这些还只是 ReactiveCocoa 的冰山一角,它真正强大的地方在于我们可以对这些不同的信号进行任意地组合和链式操作,从最原始的输入 input 开始直至得到最终的输出 output 为止:
[[[RACSignal
combineLatest:@[ RACObserve(self, username), RACObserve(self, password) ]
reduce:^(NSString *username, NSString *password) {
return @(username.length & 0 && password.length & 0);
distinctUntilChanged]
subscribeNext:^(NSNumber *valid) {
if (valid.boolValue) {
因此,对于 ReactiveCocoa 来说,我们可以毫不夸张地说,阻碍它发挥的瓶颈就只剩下你的想象力了。
在 ReactiveCocoa 中,信号源代表的是随着时间而改变的值流,这是对 ReactiveCocoa 最精准的概括,订阅者可以通过订阅信号源来获取这些值:
你可以把它想象成水龙头中的水,当你打开水龙头时,水源源不断地流出来;你也可以把它想象成电,当你插上插头时,电静静地充到你的手机上;你还可以把它想象成运送玻璃珠的管道,当你打开阀门时,珠子一个接一个地到达。这里的水、电、玻璃珠就是我们所需要的值,而打开水龙头、插上插头、打开阀门就是订阅它们的过程。
RACStream 是 ReactiveCocoa 中最核心的类,代表的是任意的值流,它是整个 ReactiveCocoa 得以建立的基石,下面是它的继承结构图:
事实上,RACStream 是一个抽象类,通常情况下,我们并不会去实例化它,而是直接使用它的两个子类 RACSignal 和 RACSequence 。那么,问题来了,为什么 RACStream 会被设计成一个抽象类?或者说它的抽象过程是以什么作为依据的呢?
是的,没错,看过我上一篇文章 《Functor、Applicative 和 Monad》 的同学,应该已经知道了,RACStream 就是以 Monad 的概念为依据进行设计的,它代表的就是一个 Monad :
An abstract class representing any stream of values.
This class represents a monad, upon which many stream-based operations can
When subclassing RACStream, only the methods in the main @interface body need
to be overridden.
@interface RACStream : NSObject
Lifts `value` into the stream monad.
Returns a stream containing only the given value.
+ (instancetype)return:(id)value;
Lazily binds a block to the values in the receiver.
This should only be used if you need to terminate the bind early, or close
over some state. -flattenMap: is more appropriate for all other cases.
block - A block returning a RACStreamBindBlock. This block will be invoked
each time the bound stream is re-evaluated. This block must not be
nil or return nil.
Returns a new stream which represents the combined result of all lazy
applications of `block`.
- (instancetype)bind:(RACStreamBindBlock (^)(void))
有了 Monad 作为基石后,许多基于流的操作就可以被建立起来了,比如 map 、filter 、zip 等。
RACSignal 代表的是未来将会被传送的值,它是一种 push-driven 的流。RACSignal 可以向订阅者发送三种不同类型的事件:
● next :RACSignal 通过 next 事件向订阅者传送新的值,并且这个值可以为 nil ;
● error :RACSignal 通过 error 事件向订阅者表明信号在正常结束前发生了错误;
● completed :RACSignal 通过 completed 事件向订阅者表明信号已经正常结束,不会再有后续的值传送给订阅者。
注意,ReactiveCocoa 中的值流只包含正常的值,即通过 next 事件传送的值,并不包括 error 和 completed 事件,它们需要被特殊处理。通常情况下,一个信号的生命周期是由任意个 next 事件和一个 error 事件或一个 completed 事件组成的。
从前面的类图中,我们可以看出,RACSignal 并非只有一个类,事实上,它的一系列功能是通过类簇来实现的。除去我们将在下节介绍的 RACSubject 及其子类外,RACSignal 还有五个用来实现不同功能的私有子类:
● RACEmptySignal :空信号,用来实现 RACSignal 的 +empty 方法;
● RACReturnSignal :一元信号,用来实现 RACSignal 的 +return: 方法;
● RACDynamicSignal :动态信号,使用一个 block 来实现订阅行为,我们在使用 RACSignal 的 +createSignal: 方法时创建的就是该类的实例;
● RACErrorSignal :错误信号,用来实现 RACSignal 的 +error: 方法;
● RACChannelTerminal :通道终端,代表 RACChannel 的一个终端,用来实现双向绑定。
对于 RACSignal 类簇来说,最核心的方法莫过于 -subscribe: 了,这个方法封装了订阅者对信号源的一次订阅过程,它是订阅者与信号源产生联系的唯一入口。因此,对于 RACSignal 的所有子类来说,这个方法的实现逻辑就代表了该子类的具体订阅行为,是区分不同子类的关键所在。同时,这也是为什么 RACSignal 中的 -subscribe: 方法是一个抽象方法,并且必须要让子类实现的原因:
- (RACDisposable *)subscribe:(id&RACSubscriber&)subscriber {
NSCAssert(NO, @"This method must be overridden by subclasses");
return nil;
RACSubject
RACSubject 代表的是可以手动控制的信号,我们可以把它看作是 RACSignal 的可变版本,就好比 NSMutableArray 是 NSArray 的可变版本一样。RACSubject 继承自 RACSignal ,所以它可以作为信号源被订阅者订阅,同时,它又实现了 RACSubscriber 协议,所以它也可以作为订阅者订阅其他信号源,这个就是 RACSubject 为什么可以手动控制的原因:
根据官方的
中的说法,我们应该尽可能少地使用它。因为它太过灵活,我们可以在任何时候任何地方操作它,所以一旦过度使用,就会使代码变得非常复杂,难以理解。
根据我的实际使用经验,在 MVVM 中使用 RACSubject 可以非常方便地实现统一的错误处理逻辑。比如,我们可以在 viewModel 的基类中声明一个 RACSubject 类型的属性 errors ,然后在 viewController 的基类中编写统一的错误处理逻辑:
[self.viewModel.errors subscribeNext:^(NSError *error) {
此时,假设在某个界面的 viewModel 中有三个用来请求远程数据的命令,分别是 requestReadmeMarkdownCommand 、requestBlobCommand 和 requestReadmeHTMLCommand ,那么这个界面的错误处理逻辑就可以这么写:
[[RACSignal
self.requestReadmeMarkdownCommand.errors,
self.requestBlobCommand.errors,
self.requestReadmeHTMLCommand.errors
subscribe:self.errors];
另外,RACSubject 也有三个用来实现不同功能的子类:
● RACGroupedSignal :分组信号,用来实现 RACSignal 的分组功能;
● RACBehaviorSubject :重演最后值的信号,当被订阅时,会向订阅者发送它最后接收到的值;
● RACReplaySubject :重演信号,保存发送过的值,当被订阅时,会向订阅者重新发送这些值。
● RACSubject 的功能非常强大,但是太过灵活,也正是因为如此,我们只有在迫不得已的情况下才会使用它。
RACSequence
RACSequence 代表的是一个不可变的值的序列,与 RACSignal 不同,它是 pull-driven 类型的流。从严格意义上讲,RACSequence 并不能算作是信号源,因为它并不能像 RACSignal 那样,可以被订阅者订阅,但是它与 RACSignal 之间可以非常方便地进行转换。
从理论上说,一个 RACSequence 由两部分组成:
● tail :指的是序列中除第一个对象外的其它所有对象,同样的,如果序列为空,则为 nil 。
Represents an immutable sequence of values. Unless otherwise specified, the
sequences' values are evaluated lazily on demand. Like Cocoa collections,
sequences cannot contain nil.
Most inherited RACStream methods that accept a block will execute the block
_at most_ once for each value that is evaluated in the returned sequence.
Side effects are subject to the behavior described in
+sequenceWithHeadBlock:tailBlock:.
Implemented as a class cluster. A minimal implementation for a subclass
consists simply of -head and -tail.
@interface RACSequence : RACStream &NSCoding, NSCopying, NSFastEnumeration&
The first object in the sequence, or nil if the sequence is empty.
Subclasses must provide an implementation of this method.
@property (nonatomic, strong, readonly)
All but the first object in the sequence, or nil if the sequence is empty.
Subclasses must provide an implementation of this method.
@property (nonatomic, strong, readonly) RACSequence *
总的来说,RACSequence 存在的最大意义就是为了简化 Objective-C 中的集合操作:
- Simplifying Collection Transformations: Higher-order functions like map, filter, fold/reduce are sorely missing from Foundation.
比如下面的代码:
NSMutableArray *results = [NSMutableArray array];
for (NSString *str in strings) {
if (str.length & 2) {
NSString *newString = [str stringByAppendingString:@"foobar"];
[results addObject:newString];
可以用 RACSequence 来优雅地实现:
RACSequence *results = [[strings.rac_sequence
filter:^ BOOL (NSString *str) {
return str.length &= 2;
map:^(NSString *str) {
return [str stringByAppendingString:@"foobar"];
因此,我们可以非常方便地使用 RACSequence 来实现集合的链式操作,直到得到你想要的最终结果为止。除此之外,使用 RACSequence 的另外一个主要好处是,RACSequence 中包含的值在默认情况下是懒计算的,即只有在真正用到的时候才会被计算,并且只会计算一次。也就是说,如果我们只用到了一个 RACSequence 中的部分值的时候,它就在不知不觉中提高了我们应用的性能。
同样的,RACSequence 的一系列功能也是通过类簇来实现的,它共有九个用来实现不同功能的私有子类:
● RACUnarySequence :一元序列,用来实现 RACSequence 的 +return: 方法;
● RACIndexSetSequence :用来遍历索引集;
● RACEmptySequence :空序列,用来实现 RACSequence 的 +empty 方法;
● RACDynamicSequence :动态序列,使用 blocks 来动态地实现一个序列;
● RACSignalSequence :用来遍历信号中的值;
● RACArraySequence :用来遍历数组中的元素;
● RACEagerSequence :非懒计算的序列,在初始化时立即计算所有的值;
● RACStringSequence :用来遍历字符串中的字符;
● RACTupleSequence :用来遍历元组中的元素。
RACSequence 为类簇提供了统一的对外接口,对于使用它的客户端代码来说,完全不需要知道私有子类的存在,很好地隐藏了实现细节。另外,值得一提的是,RACSequence 实现了快速枚举的协议 NSFastEnumeration ,在这个协议中只声明了一个看上去非常抽筋的方法:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)
有兴趣的同学,可以看看 RACSequence 中的相关实现,我们将会在后续的文章中进行介绍。因此,我们也可以直接使用 for in 来遍历一个 RACSequence 。
现在,我们已经知道信号源是什么了,为了获取信号源中的值,我们需要对信号源进行订阅。在 ReactiveCocoa 中,订阅者是一个抽象的概念,所有实现了 RACSubscriber 协议的类都可以作为信号源的订阅者。
RACSubscriber
在 RACSubscriber 协议中,声明了四个必须实现的方法:
Represents any object which can directly receive values from a RACSignal.
You generally shouldn't need to implement this protocol. +[RACSignal
createSignal:], RACSignal's subscription methods, or RACSubject should work
for most uses.
Implementors of this protocol may receive messages and values from multiple
threads simultaneously, and so should be thread-safe. Subscribers will also
be weakly referenced so implementations must allow that.
@protocol RACSubscriber &NSObject&
Sends the next value to subscribers.
value - The value to send. This can be `nil`.
- (void)sendNext:(id)value;
Sends the error to subscribers.
error - The error to send. This can be `nil`.
This terminates the subscription, and invalidates the subscriber (such that
it cannot subscribe to anything else in the future).
- (void)sendError:(NSError *)
Sends completed to subscribers.
This terminates the subscription, and invalidates the subscriber (such that
it cannot subscribe to anything else in the future).
- (void)sendC
Sends the subscriber a disposable that represents one of its subscriptions.
A subscriber may receive multiple disposables if it gets subscribed to
however, any error or completed events must terminate _all_
subscriptions.
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)
其中 -sendNext: 、-sendError: 和 -sendCompleted 分别用来从 RACSignal 接收 next 、error 和 completed 事件,而 -didSubscribeWithDisposable: 则用来接收代表某次订阅的 disposable 对象。
订阅者对信号源的一次订阅过程可以抽象为:通过 RACSignal 的 -subscribe: 方法传入一个订阅者,并最终返回一个 RACDisposable 对象的过程:
注意:在 ReactiveCocoa 中并没有专门的类 RACSubscription 来代表一次订阅,而间接地使用 RACDisposable 来充当这一角色。因此,一个 RACDisposable 对象就代表着一次订阅,并且我们可以用它来取消这次订阅,详细内容将会在下面的章节中进行介绍。
除了 RACSignal 的子类外,还有两个实现了 RACSubscriber 协议的类,如下图所示:
其中,RACSubscriber 类的名字与 RACSubscriber 协议的名字相同,这跟 Objective-C 中的 NSObject 类的名字与 NSObject 协议的名字相同是一样一样的,除了名字相同外,然并卵。通常来说,RACSubscriber 类充当的角色就是信号源的真正订阅者,它老老实实地实现了 RACSubscriber 协议。
既然 RACSubscriber 类就是真正的订阅者,那么 RACPassthroughSubscriber 类又是干嘛用的呢?原来,在 ReactiveCocoa 中,一个订阅者是可以订阅多个信号源的,也就是说它会拥有多个 RACDisposable 对象,并且它可以随时取消其中任何一个订阅。为了实现这个功能,ReactiveCocoa 就引入了 RACPassthroughSubscriber 类,它是 RACSubscriber 类的一个装饰器,封装了一个真正的订阅者 RACSubscriber 对象,它负责转发所有事件给这个真正的订阅者,而当此次订阅被取消时,它就会停止转发:
- (void)sendNext:(id)value {
if (self.disposable.disposed) return;
[self.innerSubscriber sendNext:value];
- (void)sendError:(NSError *)error {
if (self.disposable.disposed) return;
[self.innerSubscriber sendError:error];
- (void)sendCompleted {
if (self.disposable.disposed) return;
[self.innerSubscriber sendCompleted];
事实上,在 ReactiveCocoa 中,我们倾向于隐藏订阅者,因为外界根本不需要知道订阅者的存在,这是内部的实现细节。这样做的主要目的是进一步简化信号源的订阅逻辑,客户端代码只需要关心它所需要的值就可以了,根本不需要关心内部的订阅过程。
RACMulticastConnection
通常来说,我们在订阅一个信号源的过程中可能会产生副作用或者消耗比较大的资源,比如修改全局变量、发送网络请求等。这个时候,我们往往需要让多个订阅者之间共享一次订阅,就好比我们读高中时,多个好朋友一起订阅一份英语周报,然后只要出一份钱,是一个道理。这就是 ReactiveCocoa 中引入 RACMulticastConnection 类的原因。
RACMulticastConnection 通过一个标志 _hasConnected 来保证只对 sourceSignal 订阅一次,然后对外暴露一个 RACSubject 类型的 signal 供外部订阅者订阅。这样一来,不管外部订阅者对 signal 订阅多少次,我们对 sourceSignal 的订阅至多只会有一次:
注:了解 RACMulticastConnection 的实现原理,对于我们后面理解 -replay 、replayLast 和 replayLazily 等方法非常有帮助。
有了信号源和订阅者,我们还需要由调度器来统一调度订阅者订阅信号源的过程中所涉及到的任务,这样才能保证所有的任务都能够合理有序地执行。
RACScheduler
RACScheduler 在 ReactiveCocoa 中就是扮演着调度器的角色,本质上,它就是用 GCD 的串行队列来实现的,并且支持取消操作。是的,在 ReactiveCocoa 中,并没有使用到 NSOperationQueue 和 NSRunloop 等技术,RACScheduler 也只是对 GCD 的简单封装而已。
同样的,RACScheduler 的一系列功能也是通过类簇来实现的,除了用来测试的子类外,总共还有四个私有子类:
咋看之下,RACScheduler 的儿子貌似还不少,但是真正出力干活的却真心不多,主要就是 RACTargetQueueScheduler 子类:
● RACImmediateScheduler :立即执行调度的任务,这是唯一一个支持同步执行的调度器;
● RACQueueScheduler :一个抽象的队列调度器,在一个 GCD 串行列队中异步调度所有任务;
● RACTargetQueueScheduler :继承自 RACQueueScheduler ,在一个以一个任意的 GCD 队列为 target 的串行队列中异步调度所有任务;
● RACSubscriptionScheduler :一个只用来调度订阅的调度器。
值得一提的是,在 RACScheduler 中有一个非常特殊的方法:
- (RACDisposable *)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveB
这个方法的作用非常有意思,它可以将递归调用转换成迭代调用,这样做的目的是为了解决深层次的递归调用可能会带来的堆栈溢出问题。
正如我们前面所说的,在订阅者订阅信号源的过程中,可能会产生副作用或者消耗一定的资源,所以当我们在取消订阅或者完成订阅时,我们就需要做一些资源回收和垃圾清理的工作。
RACDisposable
RACDisposable 在 ReactiveCocoa 中就充当着清洁工的角色,它封装了取消和清理一次订阅所必需的工作。它有一个核心的方法 -dispose ,调用这个方法就会执行相应的清理工作,这有点类似于 NSObject 的 -dealloc 方法。RACDisposable 总共有四个子类,它的继承结构图如下:
● RACSerialDisposable :作为 disposable 的容器使用,可以包含一个 disposable 对象,并且允许将这个 disposable 对象通过原子操作交换出来;
● RACKVOTrampoline :代表一次 KVO 观察,并且可以用来停止观察;
● RACCompoundDisposable :跟 RACSerialDisposable 一样,RACCompoundDisposable 也是作为 disposable 的容器使用。不同的是,它可以包含多个 disposable 对象,并且支持手动添加和移除 disposable 对象,有点类似于可变数组 NSMutableArray 。而当一个 RACCompoundDisposable 对象被 disposed 时,它会调用其所包含的所有 disposable 对象的 -dispose 方法,有点类似于 autoreleasepool 的作用;
● RACScopedDisposable :当它被 dealloc 的时候调用本身的 -dispose 方法。
咋看之下,RACDisposable 的逻辑似乎有些复杂,不过换汤不换药,不管它们怎么换着花样玩,最终都只是为了能够在合适的时机调用 disposable 对象的 -dispose 方法,执行清理工作而已。
至此,我们介绍完了 ReactiveCocoa 的四大核心组件,对它的架构有了宏观上的认识。它建立于 Monad 的概念之上,然后围绕其搭建了一系列完整的配套组件,它们共同支撑了 ReactiveCocoa 的强大功能。尽管,ReactiveCocoa 是一个重型的函数式响应式框架,但是它并不会对我们现有的代码构成侵略性,我们完全可以在一个单独的类中使用它,哪怕只是简单的一行代码,也是没有问题的。所以,如果你对 ReactiveCocoa 感兴趣的话,不妨就从现在开始尝试吧,Let’s go !
PS: 是我用 MVVM + RAC 编写的一个开源应用,如果你有兴趣的话不妨 clone 下来看看 ReactiveCocoa 的具体实践吧。
(网上公认的关于RAC的好文章,感谢雷纯锋)
更多相关地址:
本文已收录于以下专栏:
相关文章推荐
本文主要是RAC宏展开解析:
RAC() 可以将Signal发出事件的值赋值给某个对象的某个属性,其参数为对象名和属性名,平时用到的宏如:
RAC(TARGET, [KEYPATH, [NIL_VA...
HttpClient源码解析系列:第一篇:总览。
最最核心的HttpClient接口。
转自[(/database/280.html)]
前言:一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料...
转载自:/annsshadow/p/5037667.html
  一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎...
原文地址:/database/280.html
一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就...
语言切换器
语言切换器 ::该模块允许在前台(网站前端)显示一个条目来切换到指定的语言。
语言切换器
1.Af_inet.c
这三者里面的定义可能分布在不同文件,这三种的区别是?
3.Arp.c :   arp相关
4. datagram
只有一个函数
spark源码结构结构总图org.jivesoftware.spark.ui这是spark的一个大型包。。。其实我判断大型小型只是按照文件多少的。好吧……
请问这么长的图怎么截的,我会告诉你这是...
Fs目录说明
Aio.c:异步io相关函数
Buffer.c:
1.      高速缓冲区,深入linux内核第十五章中的相关函数
2.     &#...
他的最新文章
讲师:汪剑
讲师:刘道宽
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 racreplaysubject 的文章

 

随机推荐