RAC 入门

原文:了解这些,学习RAC不再难

 

ReactiveCocoagithub 开源的一个函数式响应式编程框架,是在 iOS 平台上对 FRP 的实现。FRP 的核心是信号,信号在 RAC 中是通过 RACSignal 来表示的,信号是数据流,可以被绑定和传递。主要吸取了 .NetReactive Extensions 的设计和实现。

函数式编程 Functional Programming、响应式编程 Reactive Programming

 

一、作用

iOS 开发过程中,当某些事件响应时需要处理某些业务逻辑,这些事件都用不同的方式来处理。比如按钮的点击使用 actionScroll 滚动使用 delegate,属性值改变使用 KVO 等系统提供的方式。

其实这些事件,都可以通过 RAC 处理。ReactiveCocoa 为事件提供了很多处理方法,而且利用 RAC 处理事件很方便,可以把要处理的事情和监听事件的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合开发中高聚合、低耦合的思想。

ReactiveCocoa 主要解决了以下这些问题:

  • UI 数据绑定

UI 控件通常需要绑定一个事件,RAC 可以很方便的绑定任何数据流到控件上。

  • 用户交互事件绑定

RAC 为可交互的 UI 控件提供了一系列能发送 Signal 信号的方法。这些数据流会在用户交互中相互传递。

  • 解决状态以及状态之间依赖过多的问题

有了 RAC 的绑定之后,可以不用再关心各种复杂的状态,isSelectisFinish...也解决了这些状态在后期维护的问题。

  • 消息传递机制的统一

OC 中编程原来消息传递机制有以下几种:DelegateBlock CallbackTarget-ActionTimersKVO,现在有了RAC 之后,以上这 5 种方式都可以统一用 RAC 来处理。objc 上有一篇关于 OC 中这 5 种消息传递方式该如何选择的文章Communication Patterns,推荐大家阅读。

使用 RAC 解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。

 

ReactiveCocoa 比较复杂,在正式开始介绍它的核心组件前,我们先来看看它的类图,以便从宏观上了解它的层次结构:

image

ReactiveCocoa 主要包含四个组件:

  • 信号源:RACStream 及其子类;
  • 订阅者:RACSubscriber 的实现类及其子类;
  • 调度器:RACScheduler 及其子类;
  • 清洁工:RACDisposable 及其子类。

而信号源是最核心的部分,其它所有组件都是围绕它运作的。

 

二、信号源

信号分为冷信号与热信号,理解冷信号与热信号的区别,对于 RAC 的理解有非常大的帮助,所以这篇文章也重点讲解这里。

①、Hot Observable主动的,尽管你并没有订阅事件,但是它会时刻推送;Cold Observable被动的,只有当你订阅的时候,它才会发布消息。

②、Hot Observable 可以有多个订阅者,是一对多,集合可以与订阅者共享信息;Cold Observable只能一对一,当有不同的订阅者,消息是重新完整发送。

RAC 中除了 RACSubject 及其子类是热信号外,其它都是冷信号。subject 类似“直播”,错过了就不再处理。而 signal 类似“点播”,每次订阅都会从头开始。所以我们有理由认定 subject 天然就是热信号。

Subject 具备如下三个特点:

  • Subject 是“可变”的。
  • Subject 是非 RACRAC 的一个桥梁。
  • Subject 可以附加行为,例如 RACReplaySubject 具备为未来订阅者缓冲事件的能力。

为了了解热信号与冷信号的区别,用两段代码来展示一下:

    // 创建热信号
    RACSubject *subject = [RACSubject subject];

    // 立即发送 1
    [subject sendNext:@1];    

    // 0.5 秒后发送 2
    [[RACScheduler mainThreadScheduler] afterDelay:0.5 schedule:^{
        [subject sendNext:@2];
    }];

    // 2 秒后发送 3
    [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
        [subject sendNext:@3];     
    }];

    // 0.1秒后 subject1 订阅了
    [[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{
        [subject subscribeNext:^(id x) {
            NSLog(@"subject1接收到了%@",x);    
        }];
    }];

    // 1 秒后 subject2 订阅了
    [[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
        [subject subscribeNext:^(id x) {
            NSLog(@"subject2接收到了%@",x);        
        }];
    }];

    // 创建冷信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@1];
        [[RACScheduler mainThreadScheduler] afterDelay:0.5 schedule:^{
            [subscriber sendNext:@2];
        }];
        [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
            [subscriber sendNext:@3];
        }];
        return nil;
    }];

    [[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{
        [signal subscribeNext:^(id x) {
            NSLog(@"signal1接收到了%@", x);
        }];
    }];
    [[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
        [signal subscribeNext:^(id x) {
            NSLog(@"signal2接收到了%@", x);
        }];
    }];

上面两段代码的输出

2018-02-20 11:02:24.980462+0800 RACTest[14912:16295891] subject1接收到了2
2018-02-20 11:02:26.480232+0800 RACTest[14912:16295891] subject1接收到了3
2018-02-20 11:02:26.480408+0800 RACTest[14912:16295891] subject2接收到了3

2018-02-20 11:20:53.952995+0800 RACTest[15075:16311621] signal1接收到了1
2018-02-20 11:20:54.456881+0800 RACTest[15075:16311621] signal1接收到了2
2018-02-20 11:20:54.457046+0800 RACTest[15075:16311621] signal2接收到了1
2018-02-20 11:20:54.853391+0800 RACTest[15075:16311621] signal2接收到了2
2018-02-20 11:20:55.356641+0800 RACTest[15075:16311621] signal1接收到了3
2018-02-20 11:20:55.356851+0800 RACTest[15075:16311621] signal2接收到了3

从输出中我们可以发现:

  • 0.1 秒后订阅的 subject1 接收到了 0.5 秒后、2 秒后发送的信号,没有接收到立即发送的信号。
  • 1 秒后订阅的 subject2 接收到了 2 秒后发送的信号,也没有接收到立即发送的信号。
  • signal1signal2 都接收到了所有信号。

从中可以得出结论:

  1. 热信号是主动的,即使你没有订阅事件,它仍然会时刻推送。如上面没有接收到的信号都是因为在没有订阅者的时候,它也会推送出去。而冷信号是被动的,只有当你订阅的时候,它才会发送消息。如第二段代码,订阅后才把信号推送出去。
  2. 热信号可以有多个订阅者,是一对多,信号可以与订阅者共享信息。如第一段代码,订阅者1和订阅者2是共享的,他们都能在同一时间接收到3这个值。而冷信号只能一对一,当有不同的订阅者,消息会从新完整发送。如第一个例子,我们可以观察到两个订阅者没有联系,都是基于各自的订阅时间开始接收消息的。

 

二、将冷信号转变为热信号

RAC 库中对于冷信号转化成热信号有如下标准的封装:

- (RACMulticastConnection *)publish;
- (RACMulticastConnection *)multicast:(RACSubject *)subject;
- (RACSignal *)replay;
- (RACSignal *)replayLast;
- (RACSignal *)replayLazily;

如上面的第一段代码,我们可以用如下来达到同样的效果:

    RACMulticastConnection *connection = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@1];
        
        [[RACScheduler mainThreadScheduler] afterDelay:0.5 schedule:^{
            [subscriber sendNext:@2];
        }];
        
        [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
            [subscriber sendNext:@3];
        }];
        return nil;
    }] publish];

    [connection connect];

    RACSignal * signal = connection.signal;
    
    [[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{
        [signal subscribeNext:^(id x) {
            NSLog(@"这里是热信号1,接收到了%@", x);
        }];
    }];
    
    [[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
        [signal subscribeNext:^(id x) {
            NSLog(@"这里是热信号2,接收到了%@", x);
        }];
    }];

2018-02-20 11:45:38.331464+0800 RACTest[15171:16331870] 这里是热信号1,接收到了2
2018-02-20 11:45:39.830300+0800 RACTest[15171:16331870] 这里是热信号1,接收到了3
2018-02-20 11:45:39.830870+0800 RACTest[15171:16331870] 这里是热信号2,接收到了3

可以看到,现在已经是热信号了,和前面的 RACSubject 相同。

- (RACMulticastConnection *)multicast:(RACSubject *)subject 是将冷信号转换为热信号的核心。其实它的本质就是使用一个 Subject 来订阅原始信号,并让其他订阅者订阅这个 Subject,这个 Subject 就是热信号。

 

You may also like...