ReactiveCocoa 学习 - 2

关于ReactiveCooca的简介

2016-06-09 | 阅读

ReactiveCocoa与 RxSwift.

ReactiveCocoa是由Reactive Extensions(微软的Rx库)启发,并在之后也深深地受其影响的。而RxSwift是RX的正式成员,

ReactiveCocoa是受FRP启发,但开发环境是在Cocoa上,所以API更接近于Cocoa。

RAC中有冷热信号的概念,这是RAC中的一个核心特点。RX则把冷热信号统一。

Introduction

ReactiveCocoa 是一种Funcational Reactive Programming。 不是用一堆表示状态的变量,这些变量会在许多情况下被修改,而是使用一种事件流的方式,用Signal信号来表示状态,实时传递状态。

事件流已经统一了Cocoa中的常用的异步和实现处理,如:

  • Delegate methods
  • Callback blocks
  • NSNotifications
  • Control actions and responder chain events
  • Futures and promises
  • Key-value observing (KVO)

将这些事件处理统一成一种信号的形式,所以就可以声明链式处理和聚合信号。

总结,很多iOS程序,对于事件的处理和响应是基于应用状态的,随着回调的增加和状态变量的增加,处理这些事件的代码就回变得异常复杂,这就是我们使用ReactiveCocoa的原因,通过响应式的处理以消除中间状态,增加代码可读性和扩展性,降低复杂度。

Overview

在RAC的世界中,使用信号流处理事件. 信号的发送者,称为 receiver,信号的接收者,称为subscriber.

Streams

Stream,由RACStream抽象类表示,表示一个对象的一系列的值。

值可以立刻被拿到,也可以在未来被拿到,但收到值是顺序的。不可能在流上不接受到第一个值而直接获取第二个值。

Stream是monads的(在FP中表示用步骤流程表示的运算操作的结构),基于简单的基础信号进行复杂的操作。

RACStream是一个抽象基类,通过signalsequences来实现。

Signals

信号由RACSignal类表示。信号表示那些将要在未来被传递的数据。程序运行时,数据的值在信号中被传递,推送给Subscribers。用户需要去订阅这些信号获取数据。

信号发送三种不同的事件:

  • next : 传递数据流中的新的值。RACStream的操作方法只能处理next类型的事件。传递的数据可以为nil。
  • error : 表示信号完成前发生了错误。事件包含一个NSError的对象来表示具体错误。必须特别处理,因为错误并不包含在RACStream中。
  • completed : 表示信号完成,不会再有新的值被添加到Stream中。也需要特别处理,事件不会被包含在Stream中。

next的事件可以有任意数量个,但是errorcompleted事件最多只会发生一个。

Subscription

subscriber:订阅者,订阅信号,等待处理信号所发送的事件。在RAC中,一个订阅者者指继承RACSubscriber协议。

一个订阅的创建可以通过调用-subscribeNext:error:completed:等方法。订阅者持有所订阅的信号,会在信号完成或出错的时候自动释放,当然也可以手动释放Subscription

Subjects

SubjectRACSubject类表示,是一个可以手动管理的信号,也就是我们要着重讨论的热信号。

Subject可以理解为一个信号的mutable版本。

不在block中处理应用逻辑,而是将这些block发送给一个共享的Subject来处理。RACSubjectRACSignal的子类。

RACReplaySubject可以缓存Event,供以后的订阅者进行监听。

Commands

RACCommands类,创建和订阅一个信号,并监控其状态。是对信号的封装,将一个信号的状态,以executionSignals表示封装的信号,用executing表示信号执行的状态,用enabled表示信号是否可用,用errors表示信号执行中的异常。

这个属性通常与UI控件结合在一起,用enabled信号来控制控件是否可用,用executionSignals来表示控件可用时要执行的操作。

Connections

RACMulticastConnect类,表示可以在任意数量的订阅者中共享的一个信号。即是我们重点要讨论的第二个问题,冷热信号。

信号默认是冷的,即每当一个新的订阅者添加的时候,他们才开始处理事件,发送信号。这是一件可取的做法,数据会在每次订阅的时候刷新。但是对于有副作用的信号,或者操作消耗太多资源的信号(如网络请求),显然是有问题的。尤其是在RAC中,每次都信号进行的逻辑处理操作都是在订阅前一个信号。

通过RACSignal上的pulish或者multicast:方法创建一个这样的热信号。

Sequences

RACSequence表示表示一组信号,类似于NSArrayRACSequence表示一些列的信号,其有两个主要属性,id类型的head,和RACSequence类型的tail,则遍历这个列表时类似于一种递归的方式。则就体现了RACSequence的懒加载特性,如果这组Sequences中的值没有被使用,那就不会去获取这个值。

一般用于遍历数组 :

NSArray *numbers = @[@(1),@(2),@(3),@(4),@(5)];
    NSArray *result = [[[[numbers rac_sequence] 
      filter:^BOOL(NSNumber *value) {  
         return [value intValue] %2 ==0;  
      }] map:^id(NSNumber *value) {    
         long square = [value intValue] * [value intValue];
         return @(square); 
      }] array];       
NSLog(@"results = %@",result); 

Disposables

RACDisposables表示对信号的取消操作和资源的释放操作。用于取消信号的订阅,释放信号。一般网络操作和后台处理的一些耗时操作,都应该提供RACDisposables。调用RACDisposablesdispose方法,以取消正在订阅中的信号. 而当信号执行完成时,也会调用RACDisposables.

Schedulers

RACScheduler,提供一系列的执行队列,供信号按需执行操作或者发送结果。

RACScheduler类似于GCD,但是提供了取消功能,通过disposables,而且只执行串行任务。对于使用immediateScheduler创建的scheduler,不支持使用同步方法。可以看到设计是在通过一些限制来避免死锁的发生。

Value types

RAC提供了一些类在Stream中传递值。

  • RACTuple : 一个简单的固定大小的集合,可以包含nil对象(用RACTupleNil对象表示)。一般用于表示多个信号聚合时,聚合信号传递的数据的值。
  • RACUnit : 代表一个空值对象。
  • RACEvent : 代表信号的事件,即next,error,completed三种事件。通过materialize方法,将三种信号合成一种发送给订阅者来统一处理。

调试地狱

RAC通过一堆Block来实现事件流的传递,所以调试是意见很可怕的事情,在信号处理中的断点,会发现整个堆栈上全是RAC自己处理信号发送信号的操作,很难找到有用的信息,找到当前断点的上一级触发者。

RAC中推荐通过在事件流中加入副作用来进行调试。.on(event: { print ($0) }),也可以通过logEvents来输出事件流,但是这是2.5以后添加的新接口,而我们当前使用的依旧是2.5版本,因为2.5版本是纯Obejctive-C的。