DR·Kun

EasyReact 设计思维

2018-11-15  本文已影响908人  Magic_Unique

EasyReact 设计思维

在阅读源码前,可以先阅读此文来了解框架大致思维

简介

EasyReact 是美团开源的一套响应式编程开发框架。在已有的 ReactiveCocoa 里,iOS 开发上手响应式编程是一个挑战,主要原因如下:

  1. 高学习门槛
  2. 易出错
  3. 调试困难
  4. 风格不统一

EasyReact 的出现就是为了降低 iOS 的响应式编程开发难度,让 MVVM 在 iOS 开发中变得更加优雅。

类与协议

在整个框架中共有两大数据结构:节点

EasyReact 定义不同的节点和不同的,用节点连接在一起,形成节点链,在这形成不同结构的节点链中进行数据传递。

由于节点节点之间是依靠来传递数据,而传递的数据是单向的,所以便有了数据流的上下游概念

节点链

节点

节点主要工作是接收上游的的数据并存储,同时传递给下游的

上游边和下游边

上游

节点的上游一定是上游边节点数据输入。一个节点可以有多个上游边,也就是有多个数据输入口。

节点会强持有所有上游边

下游

节点的下游一定是下游边节点数据输出。一个节点可以有多个下游边,也就是多个数据输出口。

节点会弱持有所有下游边

多个上游边和多个下游边

存储数据

每个节点都有一个value属性,存储当前节点的数据。可以手动调用- setValue:方法来设置值,也可以绑定上游边,从上游自动获取新的值。

不论是通过手动赋值,还是从上游自动得到的新的值,节点都会将该值传递给所有的下游边,由下游边自己传递给接下来的节点链。

定制

EasyReact 没有提供,也不推荐定制节点。

主要工作是接收上游的节点的数据,并传递给下游的节点

不会存储上游的数据,但是可以对数据进行变换,然后传递给下游节点

上游

的上游一定是节点,并且一个有且只有一个上游节点,也就是只有一个数据入口。

会强持有上游节点

下游

分为两大类,一个是监听边,另一个是变换边

变换边是节点和节点之间数据传递时的变换边有且只有一个下游节点,也就是只有一个数据出口。变换边会弱持有下游节点

监听边是整个节点链的末端边,主要负责数据流的最后一步数据处理,一般是UIView或者ViewModel的赋值工作。所以监听边没有下游节点。监听边有一个依赖的监听对象(id)listenerlistener 会通过关联对象强持有监听边,也就是监听边的生命周期受listenr控制。

变换

数据在传递的过程中,可以在上进行一次单一变换。不同变换对应的变换边都继承一个基类,并重写接收数据方法,在方法中对数据进行变换。一个子类就只能做一次单一变换。

目前已有的变换包括:变化延迟直到变化过滤降阶搜索跳过前几取前几限流

变换组

支持多个数据进行数学计算得到一个数据得变换,但是这种变幻是多个数据入口,一个数据出口的变换。所以使用变换组来管理这些数据入口。

边组的持有关系和数据流向

在建立下游节点时,会对应建立和上游节点个数一样的变换边,同时建立一个变换组。每一个都会强持有变换组变换组会弱持有每一个变换边

其数据流向如下:

① 某个节点得到数据传递给边1边1存储了数据,并告诉数据有更新,但是的计算需要两个数据,所以啥都不干
② 另一个节点得到数据传递给边2边2存储了数据。
边2告诉组,数据更新。
④ ⑤ 向所有的索取数据后,计算出了新的数据
将计算得到的数据传递给当前边(也就是边2
边2将数据直接发送给下一个节点

定制

EasyReact 提供现有变换边类,可以根据已有的类作为模板进行变换定制和扩展。

类图

由于节点都有接收数据的能力,所以在框架中定义了一个 EZRNextReceiverprotocol 来统一接口。

同时 又区分变换边监听边变换组属于变换边里),所以又给定义了两套协议:EZRTransformEdgeEZRListenEdge

大致实现如下图:

类图

持有关系

不管是还是节点,总结的持有关系如下:

下游的对象强持有上游的对象,上游的对象弱持有下游的对象

下游对象一旦销毁,就会顺着持有链向上游依次释放对象,减少上游链的引用计数器

数据传递链

EasyReact 在传递数据的同时,还传递了一个上游节点链数组,以及一个对象作为上下文。

节点链数组

该数组是存放该数据已经经过的上游节点。每个节点在收到数据时,会在数组末尾加入节点自己,并将数据和新的数组传入到下游。

节点数组主要是防止闭环节点导致数据死循环的问题。节点在收到数据时,可以先判断数组中是否有自身,如果有,则说明该数据已处理过,应当停止处理此次流动。如果没有,则说明该数据在此节点没有处理过,需要保存并流向下游。

与 RAC 相比,节点链数组有效的防止了闭环导致的数据流死循环的错误。

上下文

上下文是一个任意类型的对象。在最上游的节点收到新的数据时,会生成一个新数据事件的上下文(意思是标记该次数据变化产生的源头、原因等等),上下文会随着数据在节点链中传输。

节点可以收到上下文,并截获,产生新的上下文对象传入下游(但是一般不会这么做)。

变换边在收到上下文时,可以通过上下文判断数据源头,并做不同的处理。

上下文在两个节点之间双向同步数据起到重要作用。如果要同步两个节点的数据,则需要在两个节点之间建立两个不同方向的。为了防止重复设置值的问题,可以通过上下文来判断数据变化的源头来决定本次是否赋值。

下图为数据、节点链、上下文的流动图(数据从节点 F 开始流动,并在 B 中停止流动)。

闭环节点的数据流

已知缺陷

节点闭环导致的循环引用

虽然节点链解决了数据流循环的问题,闭环节点导致绝对上下游变成了相对上下游,形成对象的循环引用。

美团官方允许闭环节点的情况产生,但注明了需要开发者自己手动维护闭环节点的生命周期,要在某些时刻主动断开某一条来破坏循环引用。

空容器内存占用

EasyReact 内部大量用到了弱引用容器,每一个上游对象都通过强持有弱引用容器来间接弱持有下游对象。

虽然并不会影响下游对象的生命周期,但是下游对象一旦销毁,该弱引用容器不会自动从上游对象中移除(除非上游对象也销毁)。

所以有可能在 App 运行期间,内存中存在大量这种空的弱引用容器。

与 RAC 的优劣分析

优势

美团官方对 EasyReact 和 RAC 做性能比较。发现在同样功能、同样机型下,RAC 的性能消耗是 EasyReact 的4倍以上,最高达到7.25

性能检测

EasyReact 有完整的中文官方文档,代码量也比 RAC 少了很多。非常容易学习。

EasyReact 已通过节点数组防止数据流闭环的问题,不需要开发者实现。

EasyReact 在传递数据时会附加参数,便于下游对象对数据得不同处理。

劣势

RAC 实现了一套 UIKit 常用信号,开发者可以直接用。EasyReact 并没有现成的 UIKit 节点实现,只有 KVO 的节点,不实用。

RAC 支持上行信号执行到期时间,来关闭下行订阅者的订阅。

EasyReact 统一使用下游对象持有上游对象,上游对象的生命周期依赖下游对象,无法独立销毁。
为了解决这个问题,必须保证:

  1. 监听者应当直接持有被监听者
  2. 监听者早于被监听者销毁,或者同步销毁

在 iOS MVVM 架构中,View 和 ViewModel 刚好满足上述两条要求。不管是 View 和 ViewModel 之间进行数据绑定,还是 ViewModel 内部做代码精简,都可以正常使用 EasyReact。

未来

EasyReact 提供了一套完美的,已节点为基础的数据流结构,完成了 iOS 平台上的响应式编程。但是在 UI 层并没有提供标准模板给开发者用,使得原本很牛逼的技术却变得很难上手实用。

美团官方已公开 Swift 版本的 EasyReact 即将开源,同时搭载 EasyReact 的 MVVM 架构也将要开源。我们上手所需要的 UIKit 扩展工具都在这个 MVVM Demo 中。可以期待一下。

总结

EasyReact 确实是一个容易学习,轻量级,高性能的响应式编程框架,在此基础上和 RAC 比较能甩 RAC 好几条街。但是最实用的功能还没开源出来,这使得 EasyReact 实用性大大降低。

但是既然美团已经决定开源 MVVM 架构,那就说明这个缺陷有被填补的可能。

所以可以等到 MVVM 架构开源后,再上手 EasyReact。

参考文献

美团客户端响应式框架 EasyReact 开源啦

上一篇 下一篇

猜你喜欢

热点阅读