开篇
在 iOS 开发中,或多或少都会见过 RAC 的身影,它为提高开发效率而生,在某些特定情况下开发时可以大大简化代码,并且目前来看安全可靠。如果你还没有接触 RAC,建议你工作之余稍作研究,并尝试体验一下。
关于 RAC 的解释,官方的说,ReactiveCocoa(其简称为RAC)是由 GitHub 开源的一个应用于 iOS 和 OS X 开发的新框架,其具有 函数式编程 和 响应式编程 的特性。
这是摘自花瓣网工程师博客的一幅图,可以直观的看到各个类之间的关系
接下来,我们就结合 RAC 中的类以及实际使用场景,来描述一下他的基本用法:
- RACSignal
- RACSubject
- RACSequence
- RACMulticastConnection
- RACCommand
- RAC 常用宏
- RAC-bind
- RAC-过滤
- RAC-映射
- RAC-组合
正题
基础用法
RACSignal
信号类,Signal 是 RAC 中的核心概念。当数据改变时,信号内部就会收到数据,然后发出。但是默认一个信号是冷信号,当一个信号没有订阅者(Subscriber)时它什么也不干,就像我们的函数,当一个函数写好之后并没有被调用它也什么都不会干。信号可以通过以下三种方式发送事件给订阅者。
详细介绍参考:从代理到 RACSignal
1 | // 1.创建信号 |
总结
信号类的作用:
- 只要有数据改变就会把数据包装成信号传递出去
- 只要有数据改变就会有信号发出
- 数据发出,并不是信号类发出,信号类不能发送数据
实现思路:
当一个信号被订阅,创建订阅者,并把 nextBlock 保存到订阅者里面
创建的时候会返回 [RACDynamicSignal createSignal:didSubscribe];
调用 RACDynamicSignal 的 didSubscribe
发送信号 [subscriber sendNext:value];
拿到订阅者的 nextBlock 调用
RACSubject
RACSubject 在使用中我们可以完全代替代理/通知,来简化代码。
(可变的信号): 可以连接RAC代码与非RAC代码,可以接收和主动发送信号。看了很多介绍都不推荐使用。目前我只把他当作代理使用了一下。
详细介绍参考:『可变』的热信号 RACSubject
1 | // 1. 创建信号 |
注意
- RACSubject 和 RACReplaySubject 的区别
- RACSubject 必须要先订阅信号之后才能发送信号, 而 RACReplaySubject 可以先发送信号后订阅.
- RACSubject 代码中体现为:先走TwoViewController的 sendNext,后走 ViewController 的 subscribeNext 订阅
- RACReplaySubject 代码中体现为:先走 ViewController 的 subscribeNext 订阅,后走 TwoViewController 的sendNext 可按实际情况各取所需。
RACSequence
详细介绍参考:Pull-Driven 的数据流 RACSequence
使用场景: 可以快速高效的遍历数组和字典。
1 | NSString *path = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]; |
也可以使用宏
1 | NSDictionary *dict = @{@"key":@1, @"key2":@2}; |
RACMulticastConnection
当有多个订阅者,但是我们只想发送一个信号的时候怎么办?这时我们就可以用 RACMulticastConnection,来实现。
详细介绍参考:用于多播的 RACMulticastConnection
1 | // 普通写法, 这样的缺点是:每订阅一次信号就得重新创建并发送请求,很不友好 |
1 | // 推荐做法。 使用RACMulticastConnection,无论有多少个订阅者,无论订阅多少次,只发送一个 |
RACCommand
RACCommand:RAC 中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程,比如看事件有没有执行完毕,详细介绍参考:优雅的 RACCommand
RACCommand
是一个在 ReactiveCocoa 中比较复杂的类,大多数使用 ReactiveCocoa 的人,尤其是初学者并不会经常使用它。
在很多情况下,虽然使用 RACSignal
和 RACSubject
就能解决绝大部分问题,但是 RACCommand
的使用会为我们带来巨大的便利,尤其是在与副作用相关的操作中。
使用场景:监听按钮点击,异步网络请求
1 | // 普通做法 |
1 | // 一般做法 |
1 | // 高级做法 |
1 | // switchToLatest--用于信号中信号 |
1 | // 监听事件有没有完成 |
进阶用法
RAC 常用宏
RAC 有许多强大而方便的宏。如下代码所示
1 | // RAC: 把一个对象的某个属性绑定一个信号,只要发出信号,就会把信号的内容给对象的属性赋值 |
1 | /** |
1 | // 例 textField 输入的值赋值给label,监听 label 文字改变 |
1 | /** |
1 | /** |
RAC-bind
1 | // 1.创建信号 |
总结
- bind(绑定)的使用思想和 Hook 的一样 —> 都是拦截 API 从而可以对数据进行操作,而影响返回数据
RAC-过滤
有时候我们想要过滤一些信号,这时候我们便可以用 RAC 的过滤方法。过滤方法有好多种,如下代码,从不同情况下进行了分析。
1 | // 跳跃 : 如下,skip传入 2 跳过前面两个值 |
1 | // distinctUntilChanged: -- 如果当前的值跟上一次的值一样,就不会被订阅到 |
1 | // take: 可以屏蔽一些值,去掉前面几个值---这里 take 为2 则只拿到前两个值 |
1 | //takeLast:和take的用法一样,不过他取的是最后的几个值,如下,则取的是最后两个值 |
1 | // takeUntil: 给 takeUntil 传的是哪个信号,那么当这个信号发送信号或 sendCompleted,就不能再接受源信号的内容 |
1 | // ignore: 忽略掉一些值 |
1 | // 一般和文本框一起用,添加过滤条件 |
RAC-映射
RAC的映射在实际开发中有什么用呢?比如我们想要拦截服务器返回的数据,给数据拼接特定的东西或想对数据进行操作从而更改返回值,类似于这样的情况下,我们便可以考虑用 RAC 的映射,实例代码如下
1 | - (void)map { |
1 | - (void)flatMap { |
1 | - (void)flattenMap2 { |
RAC-组合
把多个信号聚合成你想要的信号
使用场景:当多个输入框都有值的时候,按钮状态可点击
1 | // 把输入框输入值的信号都聚合成按钮是否能点击的信号 |
1 | - (void)zipWith { |
1 | // 任何一个信号请求完成都会被订阅到 |
究其本质核心就是各种 signals,也就是bind(绑定),处理相关事物的时候首先想到的就是绑定,基本大多数操作也都是围绕着信号进行的,以上不仅仅是 RAC 的基本用法,在 MVVM 中也是同样适用,只是把业务和逻辑通过 ViewModel 分离解耦,更大程度的利用了 RAC 在开发中的高效优势。