[iOS]Rx系列-为什么要使用Reactive Programming

Author Avatar
与狼同行 11月 28, 2016

本文整理自 美团臧大神的直播分享

一、App中的理想模型和实际模型

理想模型

一般来说,我们App的页面大体可以分为三个流程在制作:

  1. 请求网络获取页面详情页
  2. 显示到UI上
  3. 控件产生动作

但如果所有App开发都只遵循 这三步,App开发也太简单了,根本不需要那么多人。

实际模型

通常来说一个App详情页除了上述提到的三点,还有更多背后的需求:

1. 网络抓取前数据的处理
2. 抓取数据的API不止一个,怎么办?
如果有多个API,会有串并行规则,有可能先请求1和2的API,拿到结果再去请求第三个API,去获得第三个API的真正返回结果。
3. 显示的内容有选择性
比如某些页面有特价情况,但如果该返回结果没有特价,则该位置用原价代替,诸如此类的
4. 滚动改变导航栏变化
控件之间往往也会有相互的作用

现在我们把上述实际碰到的问题概括为三个复杂点:

  1. 异步数据拉取逻辑
  2. UI后期调整:当数据不完全时先用UI补充,待获取后补充UI、重新布置
  3. 控件相互作用

根据复杂点思考出解决方案
于是我们可以使用状态来解决上述问题:
初始化状态->改变状态->判断状态

二、命令式编程&Reactive Programming

527C7DA0-7732-4C62-B4A3-9BB95D6476CF.png
命令式编程&异步的问题:

  1. 命令式编程会在异步中使用callback来改变状态,即执行完callback才能接着执行其他命令。
    假如命令式没有异步的情况,程序逻辑都可以写在一个函数里,但只要逻辑中有一个异步,那么其返回并不在当前函数的范围内。
  2. 获取后刷新UI和异步响应

命令式编程在应对计算时,可以非常轻松的解决问题,但在面临异步时,需要设计很多的状态量,如果一个页面有2个接口,A和B要一起刷新UI,那么要在A的接口里判断B,在B的接口里判断A,无论如何封装,其本质也不会改变。

响应式编程

Pull Driver

FFB3260A-E97A-4391-9F8D-F257B7C799D5.png

1365E550-DADD-4D7D-9478-21E96C208368.png
当使用Pull Driver,上层由于有下层的引用,c就会自动获取得到11.
下面为实现Pull Driver的基本方式:


![32CDD012-5621-4B2C-8735-B5AB98D2B802.png](http://upload-images.jianshu.io/upload_images/712028-bc62a8bc5f877872.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

0AC65F5A-B70B-47A1-AAAD-538B31C202B9.png

Push Driver

下面是使用Push Driver的基本方式:

789FBE76-2F56-4BB8-A7D9-1389F300EBB0.png
Push Driver是当下层变化时,自动通知上方变化

![8BEB712B-6D27-403C-A3AE-E16FBF118C6E.png](http://upload-images.jianshu.io/upload_images/712028-26c2904af915328c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

30306BD7-E3A3-4D5F-A927-3E75EC602154.png

PULL VS PUSH

PULL的缺点是上层因为是拉取下层的变化,所以如果变化树很深,上层一旦变化,就会从下往上全部拉取一下,效率就十分差。
而且PULL Driver还有一个缺点就是如果下层发生变化,上层是不知道变化时机的,如果上层需要响应,还需要显示的通知上层多个对象变化。

Push的缺点是如果上层节点特别多,下层一个节点变化,那么会一层层的通知上去,会出现block套block的情况。
但这也是PushDriver最大的优势,底层数据变化是不需要上层显式的刷新的,这是一般UI控件非常需要的。
而TableView就相当于Pull Driver,不管底层数据如何覆盖变化,最后刷新的数据永远是最新的。

如何判断是否使用响应式框架

  1. 业务的次数:如果你的项目百分之80的场景都是if判断的场景
  2. 修改的频度:比如请求A和其他请求打包,如果请求A发生改变,那么就需要依次进行修改
  3. 并发的个数:如果你的项目拥有大量并发个数,那么就代表你需要大量if来回判断,这样项目就会千疮百孔。

#三、使用RxSwift解决实际问题
比如说经典的回调地狱:

5828EDA8-B3B8-424C-9556-3DAE7F4E949C.png
而使用了RxSwift后:
5ECBAF01-F107-4C7C-B6AD-D2843C36EAE3.png

但是RxSwift只是用来解决网络请求的问题就有一些浪费,因为它和PromiseKit基本上是一样的。

RxSwift基本原理


![DB23D587-4F91-4B58-AAD1-E592098664CF.png](http://upload-images.jianshu.io/upload_images/712028-851699d710cdb91f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

EE8FB142-4781-45A0-A5AA-7D661F1EF11C.png

7E4EED2D-0127-4E13-9C01-345FA0C7A5E6.png

![A66B1EB7-3662-4374-B6E1-E746AA3BA30F.png](http://upload-images.jianshu.io/upload_images/712028-d17f2b48db4aa828.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
667178A8-96B9-4731-84D1-65550487D0BE.png


![
![57783E12-0511-433D-862F-789CDFA95BD0.png](http://upload-images.jianshu.io/upload_images/712028-5b647cd20c62f624.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
](http://upload-images.jianshu.io/upload_images/712028-54ad9d6c9b1fe63c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)