MVC与MVVM

什么是MVC

MVC,是三个单独的词语,即 Model(模型)、View(视图)和 Controller(控制器)。

Model(模型)详解

定义

Model(模型)是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据。

作用

Model 定义了这个模块的数据模型,在代码中体现为数据管理者,Model 负责对数据进行获取及存放。

数据不可能凭空生成的,要么是从服务器上面获取到的数据,要么是本地数据库中的数据,也有可能是用户在UI上填写的表单即将上传到服务器上面存放,所以需要有数据来源。

既然 Model 是数据管理者,则自然由它来负责获取数据。

View(视图)详解

定义

View(视图)是应用程序中处理数据显示的部分,通常视图是依据模型数据创建的。

作用

View(视图),简单来说,就是我们在界面上看见的一切。

它们有一部分是我们 UI 定死的,也就是不会根据数据来更新显示的,比如一些 Logo 图片啊,这里有个按钮啊,那里有个输入框啊,一些显示特定内容的 label 啊等等。有一部分是会根据数据来显示内容的,比如 tableView 来显示好友列表啊,这个 tableView 的显示内容肯定是根据数据来显示的。

我们使用 MVC 解决问题的时候,通常是解决这些根据数据来显示内容的视图。

Controller(控制器)详解

定义

Controller(控制器)是应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

作用

Controller(控制器)是 MVC 中的数据和视图的协调者,也就是在 Controller 里面把 Model 的数据赋值给 View 来显示。或者是 View 接收用户输入的数据然后由 Controller 把这些数据传给 Model 来保存到本地或者上传到服务器。

MVC间的交互

mvc 间的交互协作图,如下图所示:

01 mvc.png

这张图把 MVC 分为三个独立的区域,并且中间用了一些线来隔开。

很有意思的设计,因为这些线似乎出现在了驾校科目一的内容中,你瞧 C 和 V 以及 C 和 M 之间的白线,一部分是虚线一部分是实线对吧,这就表明了引用关系。

C 可以直接引用 V 和 M,而 V 和 M 不能直接引用 C,至少你不能显式的在 V 和 M 的代码中去写和 C 相关的任何代码,而 V 和 M 之间则是双黄线,没错,它们俩谁也不能引用谁,你既不能在 M 里面写 V,也不能在 V 里面写 M。

哦,上面的描述有点小小的问题,你不是 “不能” 这样写,而是 “不应该” 这样写,没人能阻止你在写代码的时候在一个 M 里面去写 V,但是一旦你这样做了,那么你就违背了 MVC 的规范,你就不是在使用 MVC 了,所以这算是 MVC 的一个必要条件:使用 MVC –> M 里面没有 V 的代码。所以 M 里面没有 V 的代码就是使用 MVC 的必要条件。

View和Controller的交互

按钮点击事件,是 View 来接收的,但是处理这个事件的应该是 Controller,所以 View 把这个事件传递给了Controller,如何传递的呢,见图,看到 View 上面的 action 没有,这就是事件,看到 Controller 上面的 target 没有,这就是靶子,View 究竟要把事件传递给谁,它被规定了传递给靶子,Controller 实际上就是靶子。

只是 View 只负责传递事件,不负责关心靶子是谁。就像你是一个负责运货的少年,你唯一知道的是你要把货(action)交给上头(开发者)告诉你的那个收货的人(target),至于那个收货的人是警察还是怪兽,你都不需要关心。

这是 V 和 C 的一种交互方式,叫做 target-action。所以你看,这张图简直就是神来之笔,旁边还栩栩如生的画出了 V 对 C 的另一种传值:协议-委托。委托有两种:代理和数据源。什么是代理,就是专门处理 should、will、did 事件的委托,什么是数据源,就是专门处理 data、count 等等的委托。

Model和Controller的交互

M 是干嘛的?上面说了,M 就是数据管理者,你可以理解为它直接和数据库打交道。

这里的数据库可能是本地的,也可能是服务器上的,M 会从数据库获取数据,也可能把数据上传给数据库。M 也将提供属性或者接口来供 C 访问其持有的数据。我们就拿一个简单的需求作为例子,假如我想在一个模块中显示一段文字,这段文字是从网上获取下来的。

那么使用 MVC 的话,在 C 中肯定需要一个 UILabel(V)作为属性来显示这段文字,而这段文字由谁来获取呢,肯定是由 M 来获取了。

而获取的地方在哪里呢?通常在 C 的生命周期里面,所以往往是在 C 的一个生命周期方法比如 viewDidLoad 里面调用 M 获取数据的方法来获取数据。现在问题来了,M 获取数据的方法是异步的网络请求,网络请求结束后,C 才应该用请求下来的数据重新赋值给 V,现在的问题是,C 如何知道网络请求结束了?

这里我们一定要换一种角度去思考,我们进一步考虑 M 和 V 之间的关系:它们应该是一种同步的关系,也就是,不管任何时刻,只要 M 的值发生改变,V 的显示就应该发生改变(显示最新的 M 的内容)。

所以我们可以关注 M的值改变,而不用关心 M 的网络请求是否结束了。实际上 C 根本不知道 M 从哪去拿的数据,C 的责任是负责把 M 最新的数据赋值给 V。所以 C 应该关注的事件是:M 的值是否发生了变化。