MVVM与RAC(吹水篇)

500 查看

前言

首先MVC没什么不好,MVVM也没多么伟大,如果你愿意,可以把MVVM理解为特殊的MVC,就像等边三角形、直角三角形等与普通的三角形关系一样。MVVM是由MVC演变而来的,我们可以在MVC的基础上创建属于自己的开发模式。Massive View Controller?未必!controller作为viewmodel的管理者做好自己的事情就行(Single responsibility principle

  • 创建viewmodel
  • 管理view的生命周期
  • 管理viewmodel的交互逻辑
    • 监听view事件并传给model
    • 监听model变化并更新view

创建viewmodel很简单,在controller初始化的时候就可以做,关于复杂界面的UI完全可以抽象出一个view在内部完成然后再添加到controller。监听viewmodel事件的代码怎么写?抽成一个方法/属性,在controller中调用/赋值就可以了,这样一来controller中也就那么几句代码。如viewmodel网络层存储层服务层的配合,datasourcedelegatecontroller中分离,使用protocolcategoryaspect等。当然,分离、封装这种事儿过犹不及,要把握好度。最终的目的都是DRY(Don't repeat yourself
)
,做好自己的事、内聚、解耦、复用。然而鱼和熊掌不可兼得,怎样取舍视具体项目而定。无论MVCS还是MVVM或者MVPVIPER等等都是由MVC演变过来的,只是招式,都是为了处理model-view-controller之间的关系,不必生搬硬套,更重要的是如何处理复杂场景的业务逻辑,如何更好的DRY


MVVM

  • MVVM or MVCVM?

先说说胖瘦model。胖model中不单有数据也有处理数据的方法,瘦model中只有数据。我们常用的MVC中的model通常都是瘦model(实际上更像MVCS),而MVVM是基于胖model构建的。MVVM只有VMV自然是viewview controllerM就是胖model胖model又可拆分成modelview model。与其说MVVM,倒不如说MVCVM更为贴切。这样一来,MVVM不过是把MVCC的交互逻辑拿出来放到VM中去,从而达到Lighter View Controllers的目的。按照MVCVM来理解,MVVMcontroller中数据处理的业务逻辑转移到view model确实可以让controller更专注的做自己,但是当业务逻辑很复杂同样会使view model中凝聚大量代码,此时view model也需要做进一步的细化。

  • MVVM的核心思想

MVVM弱化controller,强调view modelviewbindcontroller中的业务逻辑尽量转移到view model中,除开管理view的生命周期不谈,controller只是起到协调view modelview的作用。view model的主要职责是处理业务逻辑并给view提供数据,view model不关心view从而解耦也方便做单元测试。另外,view model中不能有view但是可以有其他view model
用链状结构表示大概长这样:

View/ViewController -> ViewModel ->Model

OK,controller持有viewview modelview model处理业务逻辑并提供model,那么如何将model中的数据展示到view上?这就是前面提到的将view modelview进行绑定,这个操作是在controller中完成的,也就是controller的职责之一,协调view modelview

整个流程还是挺眼熟的,跟MVC很相似。

  • View Model 与 View 的绑定

view modelview绑定说白了就是让view model告诉view,现在数据变了,你该显示不同的内容。但是view model不可以持有view,因此赋值操作无法写在view model里而是在controller中完成。其实可选的方法很多,blockdelegatenotificationKVOtarget-action/invocation

如果非要说优雅这个词,不谈RAC似乎显得太low,但是就像前面所说,可供选择的方法很多,不是非RAC不可。

RAC

说起RAC不得不说Stream这个概念,stream翻译过来是的意思,用来组词我们最先想到可能是水流电流,会有动态的画面感,流动即驱动,驱动的原动力是事件流RAC就是基于stream这个概念发展来的,即所谓的函数响应式编程(
Functional Reactive Programming:FRP )

  • RACStream

RACStream有两个子类:RACSignalRACSequenceSignal表示信号Sequence表示序列,两者都可以很好的描述所拥有的特性。

信号可发送、可接收、可合并、可压缩、可增删改查、可…。这些特性流同样拥有,信号即流,万物皆流,拥有流即可改造万物。

诗云:江河入海流序列可理解为大海,海纳百川序列可以汇集,形成一个更庞大的,也可以分离出所汇集的每一个

  • RACSignal

signal存在的意义就是加工并传递数据,所以signal需要一个订阅者subscriber来接收数据。有subscribersignal称为热信号,没有subscribersignal称为冷信号,没有意义。

举个栗子:

我是栗子
文/01_Jack(简书作者)
原文链接:http://www.jianshu.com/p/39ee13d03b75
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。 

很方便吧,然而并没有什么…用!这是怎么做到的?刚才不是还说需要subscriber吗,为啥没见到?贴一下源码:

可见,这个方法内部已经配置了订阅者,所以可以直接工作。订阅者内部拷贝nexterrorcompleted这三个block,其中errorcompletedNULLnext是基于textField的UIControlEventAllEditingEvents事件触发的,block内参数为textField.text。至此,从心理上起码可以接受RAC这种东西。

凡事要学会抓主要矛盾,切勿眉毛胡子一把抓。先别管代码里种种看不明白的天书,来看看这个方法做了什么,为啥可以监听UIControlEventAllEditingEvents事件

[self rac_signalForControlEvents:UIControlEventAllEditingEvents]

看到了啥?UIControl的分类以及target-actiontargetRACSubscriber的实例,action则为RACSubscriber的协议方法- (void)sendNext:(id)value。Let’s go on!

拷贝self.next后执行nextBlock这个block。那么self.next又是啥?就是例子开始中的block

再来理一遍思路看看RAC是如何一句代码完成对textFieldUIControlEventAllEditingEvents事件监听的。

1 将textField信号化并通过block将target-action转移到订阅者
2 创建订阅者并在订阅者内部拷贝外部subscribeNext中的block
3 当触发UIControlEventAllEditingEvents事件通过订阅者回调外部block


如果你已经理解这些,我们继续吧。回到这个方法,来看看天书们是否安好

什么乱七八糟的。。!先别想这么多,试着结合上文把刚刚看到的注释拼接起来带入- (RACSignal *)rac_textSignal中可以得到这么一句话:

创建一个可变为热信号的冷信号,并将这个信号传递给textField,当信号传递完毕,利用target-action让订阅者监听UIControlEventAllEditingEvents事件,映射当前信号并作将textField.text做为block参数返回,这一系列操作直到textField.rac_willDeallocSignal执行next或者completed时结束。

那么这句代码的意思已经很清楚了