模型-视图-控制器(Model-View-Controller,MVC)是Xerox PARC在20世纪80年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已广泛应用于用户交互应用程序中。在iOS开发中MVC的机制被使用的淋漓尽致,充分理解iOS的MVC模式,有助于我们程序的组织合理性。
首先,如何看懂上图,在M/V/C之间有3种线性,黄线、白色实线、白色虚线:
黄线:在M和V之间有两条黄线,这表示什么呢?它意味着你不能穿越这黄线,任何一个方向都不行。
白色虚线:意味着这个方向可以任意通过,即C可以随意访问V和M;
白色实线:意味着V和M可以访问C,但是是需要采取迂回的一些方式,不能直接访问;
我们来看C和M之间的绿色箭头,这箭头的方向就代表着“发起对话”的方向,也就是说,发起对话的是C,而做出回答的是M。C可以问M各种各样的问题,但M只是回答C的问题或要求,它不可以主动的向C要求什么。还记得虚线是畅通无阻的意思吧,所以,C知道M的所有的事情,如果用代码来说明这件事情,就是说,C可以导入M的头文件或是M的接口(API)。因为C可以通过M的API,所以它就可以肆无忌惮的向M要求这要求那了。
(简单来说:控制器可以随意获取模型数据)
我们再来看看另外的一个绿色箭头,它是在C和V之间,和前一个绿色箭头的意义一样,它代表C可以直接地向V进行交流。你可以想想,C要把V放到屏幕上,并设置V的属性,告诉它们什么时候从屏幕上消失,把它们分成组等等。如果C不能自由的向V发号施令的话,程序的显示将会多么的困难,所以,C可以毫无限制地向V说话。
(简单来说:控制器可以随意修改视图的样式)
V对C的交流有三种不同的方式。
第一种我们称为目标操作(target-action)。它是这样工作的,C会在自己的内部“悬挂”一个目标(target),如图中的红白相间的靶子,对应的,它还会分发一个操作(action,如图中的黄色箭头)给将要和它交流的视图对象(可能是屏幕上的一个按钮),当按钮被按时,action就会被发送给与之对应的target,这样V就可以和C交流了。但是在这种情况下,V只是知道发送action给对应的target,它并不知道C中的类,也不知道它到底发送了什么。target-action是我们经常使用的方法。
第二种方式我们叫做委托(delegate)。有时候,V需要和C进行同步,你知道,用户交互不仅仅是什么按按钮,划滑块,还有很多种形式。好了,让我们来看看图中的delegate黄色箭头,你发现箭头上又分出了四个小箭头:should,did,will,还有一个没标注的。绝大部分的delegate信息都是should,will,did这三种形式。和英文意思相对应,should代表视图对象将询问C中的某个对象“我应该这么做么?”,举个例子,有一个web视图,有人点击了一个链接,web视图就要问“我应该打开这个链接么?这样做安全么?”。这就是should信息。那will和did呢?will就是“我将要做这件事了”,did就是“我已经做了这件事”。C把自己设置为V的委托(delegate),它让V知道:如果V想知道更多的关于将如何显示的信息的话,就向C发送delegate信息。通过接受V发过来的delegate信息,C就会做出相应的协调和处理。还有一点,每个V只能有一个delegate。
第三种方式就是数据源(datasource),你知道,V不能拥有它所要显示的数据,记住这点非常重要。V希望别人帮助它管理将要显示的数据,当它需要数据时,它就会请求别人的帮助,把需要的数据给它。
你看到M和C之间的白线,这意味着M不可以直接地,没有限制的对C进行交流。但有时,这个方向的交流是必要的。当M中的一些东西发生变化时,C需要了解这些变化,那我们怎么才能让C知道M的变化呢?通知(Notification)和KVO是解决问题的好方法。它们是这样工作的,当M中的某些东西发生变化时,他们会向C发出通知“嘿,老兄,注意了啊,我这发生变化了”,或者他们会发出指向变化的指针给C,或其他什么的。总之,他们的工作模式是这样的。当C收到M发送的通知后,会去更新视图V的显示。
那M和V之间可以交流么?还记得黄线的意思么?完全不可以通过,所以我们是不允许M和V进行交流的。这是因为我们不希望这三部分之间有过多的交流,你想想,假如V在显示时出现了问题,比如有一个图形没有显示出来,我们就要去查找错误,因为C可以和V交流,M也可以和V交流的话,我们就要去检查两个部分。相反的,只有C可以和V交流的话,在出错时,我们就只需要去C那里查找原因,这样查找错误不就很是简单了么?所以,我们不允许M和V之间有直接的联系,这也是在它们两之间有两根黄线的原因。
MVVM是微软首先提出来的一种设计模式。在开发过程中随着代码量越来越多,维护的时候,暴露出来的问题也越来越多。这些问题中就包括UI和业务逻辑的高耦合,这会导致UI和业务逻辑很难进行单独修改,很难进行复用,很难进行单元测试。所以今天的主角MVVM设计模式就被创建出来了。
每一位iOS开发者,用的最多、最熟悉的设计模式估计就是MVC设计模式了。因为苹果为我们提供的框架就是使用MVC设计模式的。MVVM设计模式其实有很多概念和MVC很相似。
MVVM设计模式有三部分组成:View,View Model,Model。每一部分有自已的职责。下图是描述的它们之间的关系。
这三部分之间相互解耦,因此:
每个部分能被单独替换
修改内部的实现,不会影响别的部分
每个部分能独立工作
可以对每一部分进行单元测试
另外,为了更好的理解这三部分的职责,需要理解它们之间是如何进行交互的。从高层次上看,View“知道”View Model,而View Model并不“知道”View;View Model “知道” Model,而Model并不“知道”View Model。
View Model将View和Model隔离开来,使得Model,和View能单独进行修改。
View负责定义页面结构,布局用户在屏幕上看到的界面。 在iOS开发中,View一般是程序中的一个页面。一个View也可以是父View的一部分。
每个View都有自己的View Model,或者它从父View中继承了View Model。View通常是通过通过View Model的绑定,或者调用View Model中的方法来获取数据。在运行的时候,UI控件要响应View Model抛出通知事件,来更新UI。
为了响应UI上的交互,需要在View Model中实现相应的代码。比如一个按钮被点击。需要将这个按钮点击事件绑定到View Model中去。然后在View Model中写代码处理用户的交互。
在iOS开发中View和ViewController都是属于View这部分。
与业务和验证逻辑有关的域模型,都可以认为是MVVM中的Model。比如:业务对象,数据传输对象(DTOs),实体,代理对象等等都可以认为是Model。这里的Model与MVC中的Model一样。
View Model扮演着View和Model的中介角色,它负责处理视图逻辑和数据转化。一般来说View Model通过调用Model的方法来和Model进行交互。View Model随后将从Model中获取的数据以一种view容易使用的格式提供给View。View Model负责UI交互的响应代码实现。 例如:当用户点击了一个按钮在UI中,这个动作会使得View Model中相应代码被执行。View Model也负责控制视图的显示逻辑,比如修改UI指示操作正在进行。