MVC = Massive View Controller ?
有笑话称MVC为重量级的试图控制器。仔细一想,确实存在这个问题。以UITableViewController和UITableView举个例子。
一般情况下,我们没有必要创建一个自定义的tableview继承自UITableView。这意味着View的模块几乎为空白。(在相当多的时候,view也仅是一个xib文件或者一堆代码用来描述控件外观和控件的位置而已)。至于Model,十分类似于JavaBean,一个类,若干属性和constructor足矣。而大量的业务逻辑的处理被放在View Controller中。
MVC的目的与不足
最初我们希望,能够把视图、模型分离开来,并用一个视图控制器去管理两者。然而,理想很丰满,现实很骨感。实际编程时,遇到种种奇奇怪怪的情况。
例如:
- 涉及到网络请求放在哪里?
- 数据逻辑的转换放在哪里?比如把一个NSArray类型的数组转换成NSString对象并且展示出来。
要知道,视图控制器,是用来管理视图的。让它处理这些乱七八糟的逻辑,是不是显得太臃肿了,管了太多自己不应该负责的事情。
更何况,几乎所有的视图和视图控制器都是一一对应关系。既然视图离不开控制器,控制器也必须维护一个自己管理的视图,为什么不干脆把他们结合在一起?
新的设计结构——MVVM
简单来说。MVVM就是一个经过优化的MVC,这意味着它可以兼容,也本质上还是一个MVC结构。
所谓的MVVM架构,就是把那些Controller不应该负责的内容分离出来。单独作为一块。用MVVM的术语来说,就是把“将 Model 数据转换为 View 可以呈现的东西”这一可能极为复杂的过程剥离出来,放到viewmodel对象中。至于ViewController,嗯,你就负责管好你的视图吧。别的东西你也别负责了。(因此,网络请求也应该被剥离开来,放入viewmodel中,实际上一次网络请求,只不过是将一些不在本机存放的模型,从Json转换为可供View直接展示的数据)。
不管是从字面(viewmodel)还是从它的具体功能以及实现来理解,视图模型(viewmodel)依然是一个模型(Model),它为View提供了可以拿来使用的数据,也从模型(Model)提取数据并加以转化处理。
以一个简单的链状结构表示MVVM架构就是
View/ViewController ————> viewmodel ————> Model
这里ViewController对象直接持有一个viewmodel对象。viewmodel对象又直接持有Model对象。箭头不能被反向(这是MVVM架构的基本要求)。
这样导致了一个问题,viewmodel发生变化的时候怎么通知ViewController并且相应的改变View呢?这就是ReactiveCocoa框架要干的事情了。Model的变化怎么通知viewmodel呢?额。。。。。Model需要变么?额。。。一定非得要变的话,继续用ReactiveCocoa不就行了么。
ReactiveCocoa是何方神圣?
以C语言代码举个例子
int a = 1;
int b = 2;
int c = a + b;
printf("c = %d",c);
显然输出结果是“c = 3”。
如果改变一下代码,增加一行。
int a = 1;
int b = 2;
int c = a + b;
a++;
printf("c = %d",c);
显然输出结果依然是“c = 3”。
如果我们希望c永远等于a和b的和,注意是永远等于。那么目前看来唯一的方法是每次a和b发生变化的时候重新执行c = a + b。这样的操作麻烦透了,幸运的是,iOS开发中,OC提供了KVO机制,而ReactiveCocoa框架利用了这个机制,并且进行了各种各样的拓展。
ReactiveCocoa框架有多强?
简单来说,试想一下:一个电路中有电源、开关,还有若干个小灯泡串联在一起。断开开关,所有的灯泡直接熄灭,多么简单的一件事!因为所有灯泡的亮暗直接依赖于开关的状态。而十个人居住在十个房间里,某个人起床时必须挨个通知其他九人:“我起床了”。What the huck?
举一个实际开发的例子。假设有一个注册界面,只有当输入的用户名密码满足某种逻辑的时候,注册按钮才会变为可用状态。通常我们需要实现文本框的代理函数,对于每一次文字变动,执行判断函数再去修改按钮的状态。
在ReactiveCocoa这种动态的、响应式框架下,我们要做的,就是把button的enabled属性和username.isValid && password.isValid绑定起来就可以了。
这样的逻辑实在是太棒了,在下一章中,我们一起探索ReactiveCocoa框架的实际使用。