RAC基础笔记(2)

ReactiveCocoa 常见类

信号源相关

RACStream

An abstract class representing any stream of values.编程

RACStream 是一个抽象类,是以 Monad (函数式编程语言)的概念为依据进行设计的,它表明的就是一个 Monad 。有了 Monad 做为基石后,许多基于流的操做就能够被创建起来了,好比map 、 filter 、 zip 等。缓存

RACSignal

能够把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径同样,这样就能保证玻璃球是依次排列,不会出现并排的状况(数据都是线性处理的,不会出现并发状况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。能够在水龙头上加一个过滤嘴(filter),不符合的不让经过,也能够加一个改动装置,把球改变成符合本身的需求(map)。也能够把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),这样只要其中的一个水龙头有玻璃球出来,这个新合并的水龙头就会获得这个球。闭包

RACSignal 表明的是未来会被传递的值,它是一种 push-driven 的流。 RACSignal 能够向订阅者发送三种不一样类型的事件:并发

  • next : RACSignal 经过 next 事件向订阅者传送新的值,而且这个值能够为 nil ;
  • error : RACSignal 经过 error 事件向订阅者代表信号在正常结束前发生了错误;
  • completed : RACSignal 经过 completed 事件向订阅者代表信号已经正常结束,不会再有后续的值传送给订阅者。

注意, ReactiveCocoa 中的值流只包含正常的值,即经过 next 事件传送的值,并不包括 error 和 completed 事件,它们须要被特殊处理。一般状况下,一个信号的生命周期是由任意个next 事件和一个 error 事件或一个 completed 事件组成的。less

RACSignal 的 Subscription 过程归纳起来能够分为三个步骤:编程语言

  1. [RACSignal createSignal] 来得到 signal
  2. [signal subscribeNext:] 来得到 subscriber,而后进行subscription
  3. 进入 didSubscribe ,经过 [subscriber sendNext:] 来执行 next block 。

RACSubject

RACSubject 表明的是能够手动控制的信号,咱们能够把它看做是 RACSignal 的可变版本,就比如 NSMutableArray 是 NSArray 的可变版本同样。 RACSubject 继承自 RACSignal ,因此它能够做为信号源被订阅者订阅,同时,它又实现了 RACSubscriber 协议,因此它也能够做为订阅者订阅其余信号源,这个就是 RACSubject 为何能够手动控制的缘由。ide

实际使用中,在 MVVM 中使用 RACSubject 能够很是方便地实现统一的错误处理逻辑。好比,咱们能够在 viewModel 的基类中声明一个 RACSubject 类型的属性 errors ,而后在 viewController 的基类中编写统一的错误处理逻辑:函数式编程

[self.viewModel.errors subscribeNext:^(NSError *error) {
    // 错误处理逻辑
}

RACCommand

A command is a signal triggered in response to some action, typically UI-related.函数

RACCommand 一般用来表示某个 Action 的执行,好比点击 Button 。它有几个比较重要的属性:executionSignals / errors / executing。oop

  • executionSignals :是 signal of signals ,若是直接 subscribe 的话会获得一个 signal ,而不是咱们想要的 value,因此通常会配合 switchToLatest 。
  • errors :跟正常的 signal 不同,RACCommand 的错误不是经过 sendError 来实现的,而是经过 errors 属性传递出来的。
  • executing :表示该 command 当前是否正在执行。

RACSequence

Represents an immutable sequence of values. Unless otherwise specified, the sequences’ values are evaluated lazily on demand. Like Cocoa collections, sequences cannot contain nil.

RACSequence 表明的是一个不可变的值的序列,与 RACSignal 不一样,它是 pull-driven 类型的流。从严格意义上讲, RACSequence 并不能算做是信号源,由于它并不能像 RACSignal 那样,能够被订阅者订阅,可是它与 RACSignal 之间能够很是方便地进行转换。

所以,咱们能够很是方便地使用 RACSequence 来实现集合的链式操做,直到获得你想要的最终结果为止,经常使用的使用场景为「字典转模型」。

注意: RACSequence 会涉及到性能与效率的问题。

订阅者相关

RACSubscriber

Represents any object which can directly receive values from a RACSignal.

订阅者对信号源的一次订阅过程能够抽象为:经过 RACSignal 的 -subscribe: 方法传入一个订阅者,并最终返回一个 RACDisposable 对象的过程。

注意:在 ReactiveCocoa 中并无专门的类 RACSubscription 来表明一次订阅,而间接地使用 RACDisposable 来充当这一角色。所以,一个 RACDisposable 对象就表明着一次订阅,而且咱们能够用它来取消此次订阅。

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock { trueNSCParameterAssert(nextBlock != NULL); true trueRACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL]; truereturn [self subscribe:o]; } + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed { trueRACSubscriber *subscriber = [[self alloc] init]; truesubscriber->_next = [next copy]; truesubscriber->_error = [error copy]; truesubscriber->_completed = [completed copy]; truereturn subscriber; } - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber { trueNSCAssert(NO, @"This method must be overridden by subclasses"); truereturn nil; } 

调度器相关

RACScheduler

Schedulers are used to control when and where work is performed.

RACScheduler 在 ReactiveCocoa 中就是扮演着调度器的角色,本质上,它就是用 GCD 的串行队列来实现的,而且支持取消操做。是的,在 ReactiveCocoa 中,并无使用到 NSOperationQueue 和 NSRunloop 等技术, RACScheduler 也只是对 GCD 的简单封装而已。

清洁工相关

RACDisposable

A disposable encapsulates the work necessary to tear down and cleanup a subscription.

RACDisposable 在 ReactiveCocoa 中就充当着清洁工的角色,它封装了取消和清理一次订阅所必需的工做。它有一个核心的方法 -dispose ,调用这个方法就会执行相应的清理工做,这有点相似于 NSObject 的 -dealloc 方法。

ReactiveCocoa 常见用法

代替代理

  • rac_signalForSelector 用户代替代理

代替 KVO

  • rac_valuesAndChangesForKeyPath 用于监听某个对象的某个属性的改变

代替事件监听

  • rac_signalForControlEvents 用于监听某个事件

代替通知

  • rac_addObserverForName 用于监听某个通知,且不须要在 - (void)dealloc 中移除监听

监听文本框文字改变

  • rac_textSignal 用于监听文本框文字变化

代替手势

  • rac_gestureSignal 用于监听手势操做

多个请求完成时,再执行后继操做

  • rac_liftSelector:withSignalsFromArray:Signals 当传入的 Signals,每个 Signal 都至少 sendNext 过一次,就会去触发第一个 selector 参数的方法。

信号的相关操做

  • bind :函数会返回一个新的信号 N。总体思路是对原信号 O 进行订阅,每当信号 O 产生一个值就将其转变成一个中间信号 M ,并立刻订阅 M ,以后将信号M的输出做为新信号 N 的输出。
  • map \ flattenMap :用于把源信号内容映射成新的内容(信号)。
  • concat :组合,按必定顺序拼接信号,当多个信号发出的时候,有顺序的接收信号。
  • then :用于链接两个信号,当第一个信号完成,才会链接 then 返回的信号。
  • merge :把多个信号合并为一个信号,任何一个信号有新值的时候就会调用。
  • zipWith :把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,而且把两个信号的内容合并成一个元组,才会触发压缩流的 next 事件。
  • combineLatest :将多个信号合并起来,而且拿到各个信号的最新的值,必须每一个合并的 signal 至少都有过一次 sendNext ,才会触发合并的信号。
  • reduce :聚合,用于信号发出的内容是元组,把信号发出元组的值聚合成一个值。
  • filter :过滤信号,使用它能够获取知足条件的信号。
  • ignore :忽略某些值的信号,使用 RACObserve 时可配合使用,其实现由 filter 完成。
  • distinctUntilChanged :实现是用 bind 来完成的,每次变换中都记录一下原信号上一次发送过来的值,并与这一次进行比较,若是是相同的值,就「吞」掉,返回 empty 信号。只有和原信号上一次发送的值不一样,变换后的新信号才把这个值发送出来。
  • take :从开始一共取 N 次的信号。
  • takeLast :取最后 N 次的信号,前提条件:订阅者必须调用完成,由于只有完成,才知道总共有多少信号。
  • takeUntil :获取信号直到某个信号执行完成。
  • skip :跳过几个信号,不接受。
  • switchToLatest :用于 signalOfSignals (信号的信号),有时候信号也会发出信号,会在 signalOfSignals 中,获取 signalOfSignals 发送的最新信号。
  • doNext :执行 next 以前,会先执行这个 Block 。
  • doCompleted :执行 sendCompleted 以前,会先执行这个Block 。
  • timeout :超时,可让一个信号在必定的时间后,自动报错。
  • interval :定时:每隔一段时间发出信号。
  • delay :延迟发送 next 。
  • retry :重试,只要失败,就会从新执行建立信号中的 block ,直到成功。
  • replay :重放,当一个信号被屡次订阅,反复播放内容。
  • throttle :节流,当某个信号发送比较频繁时,可使用节流,在某一段时间不发送信号内容,过了一段时间获取信号的最新内容发出。

ReactiveCocoa 常见宏

  • RAC(TARGET, ...) 用于绑定某个对象的某个属性
  • RACObserve(TARGET, KEYPATH) 用于监听某个对象的某个属性,返回的是信号
  • @weakify(Obj) & @strongify(Obj) 配套使用

注意事项

Side Effect

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.

若是某个信号被多个 subscriber 订阅,那么它的 didSubscribe 会被屡次调用。

若是想要避免这种状况的发生,可使用 reply / replayLast / replayLazily 方法,它们的做用是保证 signal 只被触发一次,而后把 sendNext: 的 value 给缓存起来,下一次再有新的 subscriber 时,直接发送缓存的 value 。

其内部实现依赖: - (RACMulticastConnection *)multicast:(RACSubject *)subject; 这个方法。

Cell 重用

RAC 给 UITableViewCell 提供了一个方法: rac_prepareForReuseSignal ,它的做用是当 Cell 即将要被重用时,告诉 Cell 。想象 Cell 上有多个 Button ,Cell 在初始化时给每一个 Button 都 addTarget:action:forControlEvents ,被重用时须要先移除这些 target ,下面这段代码就能够很方便地解决这个问题:

[[[self.cancelButton
truerac_signalForControlEvents:UIControlEventTouchUpInside]
truetakeUntil:self.rac_prepareForReuseSignal]
truesubscribeNext:^(UIButton *x) {
true// do other things }]; 

Strong / Weak Dance

由于 RAC 不少操做都是在 Block 中进行的,因此最多见的问题即是「循环引用」,因此须要经过 @weakify 和 @strongify 来消除循环引用。

注意:事实上 RACObserve(TARGET, KEYPATH) 老是会引用 self ,即便 target 不是 self ,因此只要有 RACObserve 的地方都要使用 @weakify / @strongify 。

flattenMap 与 map 的区别

  • flattenMap 中的 block 返回信号。
  • map 中的 block 返回对象。
  • map 的实现是用了 flattenMap 函数来实现的。把 map 的入参闭包,放到了 flattenMap的返回值中。
  • 开发中,若是信号发出的值不是信号,映射通常使用 map 。
  • 开发中,若是信号发出的值是信号,映射通常使用 flattenMap 。
  • signalOfsignals 用 flattenMap 。

冷信号与热信号的区别

  • Hot Observable 是主动的,尽管你并无订阅事件,可是它会时刻推送,就像鼠标移动;而 Cold Observable 是被动的,只有当你订阅的时候,它才会发布消息。
  • Hot Observable 能够有多个订阅者,是一对多,集合能够与订阅者共享信息;而 Cold Observable 只能一对一,当有不一样的订阅者,消息是从新完整发送。
  • Subject 相似「直播」,错过了就再也不处理。而 Signal 相似「点播」。
  • RACSubject 及其子类是热信号, RACSignal 排除 RACSubject 类之外的是冷信号。
  • RACSubject 会持有订阅者(由于 RACSubject 是热信号,为了保证将来有事件发送的时候,订阅者能够收到信息,因此须要对订阅者保持状态,作法就是持有订阅者),而 RACSignal 不会持有订阅者。

其余

  • 当一个 signal 被一个 subscriber subscribe 后,这个 subscriber 什么时候会被移除?答案是:当 subscriber 被 sendComplete 或 sendError 时,或者手动调用 [disposable dispose] 。

  • replay 是 multicast 的一个特殊 case 而已。

  • 当 subscriber 被 dispose 后,全部该 subscriber 相关的工做都会被中止或取消,如 http 请求,资源也会被释放。

  • Errors 有优先权,若是有多个 signals 被同时监听,只要其中一个 signal sendError ,那么 error 就会马上被传送给 subscriber ,并致使 signals 终止执行。至关于 Exception 。

  • 使用 RACSubject ,若是进行了 map 操做,那么必定要发送完成信号,否则会内存泄漏。

  • 任何的信号转换便是对原有的信号进行订阅从而产生新的信号。

相关文章
相关标签/搜索