大家都知道MVC是软件工程中的一种架构模式,主要是吧软件系统分为三个部分:模型Model
、视图View
以及控制器Controller,该设计模式的主要目的就是将数据和视图分离开,可以简化后续对于软件系统的修改和扩展,并且能够使得某些部分进行复用,这三部分的作用职责如下:
数据
Model: 负责封装数据、存储和处理数据运算等工作在传统的MVC
结构中,数据层在发生改变之后会通知视图层进行对应的处理,视图层能直接访问数据层。但在iOS中,M
和V
之间禁止通信,彼此独立,必须由C
控制器层来协调M
和V
之间的变化。如下图所示,C持有M与V,它
对于M
和V
的访问是不受限的,但是M
和V
不允许直接接触控制器层,而是由多种通知、回调、事件触发等
方式来通知控制器的
在iOS的大多数应用程序的代码中MVC模式是这样组织的,view 将用户交互通知给控制器。view 的控制器通过更新 Model 来反应状态的改变。Model(通常使用 Key-Value-Observation)通知控制器来更新他们负责的 view。
由上图可以再次验证以下几点:
Model 和 View 永远不能相互通信,只能通过 Controller 传递。
Controller 可以直接与 Model 对话(读写调用 Model),Model 通过 Notification 和 KVO 机制与 Controller 间接通信。
Controller 可以直接与 View 对话,通过 outlet,直接操作 View,outlet 直接对应到 View 中的控件,View 通过 action 向 Controller 报告事件的发生(如用户 Touch 我了)。Controller 是 View 的直接数据源(数据很可能是 Controller 从 Model 中取得并经过加工了)。Controller 是 View 的代理(delegate),以同步 View 与 Controller。
下图是苹果推荐的MVC简单示意图,这个图是依据MVC的理论所构成的
可以看出controller所需要承担的任务是比较重的,理论始终是理论,在实际开发中常常将View和Controller部分代码全部塞到了ViewController类中,造成了View和Controller的高度耦合,这两者之间难以独立。下图是实际开发时的简单示意图。
从以上分析可以得出MVC的一些优点和缺点
对于其缺点主要就是View和Controller耦合所造成的,缺点如下:
ps:其实还衍生了一种模式MVCS,S即store存储的意思,该模式其实就是针对于MVC的一个小优化,将这部分代码单独从Model或者ViewController中抽离出来构成单独文件,即为数据存储层。那么对于之前无法放置的网络请求即可放入该层面,因为网络请求也是获取数据,而且一般API请求之后数据都需要进行缓存和持久化处理,所以可以放置在存储层。
由上可得MVC的这些弊端就是因为代码分配过于笼统,View和Controller高度耦合,所以如何解耦是一个比较关键的问题。MVP以及MVVM就是解决了MVC的这个弊端所引进的模式。
MVP即为Model-View-Presenter。同MVC来说的话,他们的Model其实是完全一样的,因为他要做的就是解耦MVC中的View和Controller,对于Model没必要进行更改。对于MVC来说,View和Controller耦合在了ViewController中,但是在MVP中,View则演变为单独的UIView/UIViewController或者说是UIView和UIViewController。MVP将MVC的ViewController进行拆分,对于视图数据逻辑处理的部分划分为Presenter单独的类,ViewController剩余部分与View合并成V。
视图数据逻辑:与视图相关的数据处理。例如将
NSDate
转换成NSString
。
由图可以看出MVP中的View持有了Presenter作为变量,当接收到用户交互信息时,会调用Presenter层暴露出的接口进行处理,也即View层不包含任何的业务逻辑代码就可以完成整个业务逻辑和页面显示,View只会将交互交给Presenter,并从Presenter中接收结果来更新自己。总的概括就是Presenter负责业务逻辑,是View和Model的桥接,会根据View中的交互修改Model或者根据Model变化修改View。
ps:View和Presenter之间往往通过Protocol进行通信。另外,因为View持有Presenter,所以对于Presenter中的View需要声明为weak或者unowned避免循环引用。
相比于MVC来说MVP实现了各模块的解耦,使得耦合度大大降低,具有更好的可测试性。但是MVP的缺点在于View的所有交互都需要交付给Presenter处理,从而一旦项目功能增加了,View的代码和Presenter的代码都会增加,总体的代码量可能会翻倍,比MVC大,使得维护成本和文件都会增大。另外,iOS MVC更适用于快速开发,即代码规模较小的项目。因此将简单的MVC的Demo改成MVP,反而会显得笨拙。
实际上MVVM模式就是在MVP的基础上将presenter改为与View双向绑定的ViewModel即可演变而成。MVVM和MVP的最大区别就是MVVM采用了双向绑定机制,View的变动,自动反映在ViewModel上。
在MVVM模式中ViewModel扮演两个重要的角色
ps:由示意图可以看出View是持有ViewModel的,在ViewModel中不能包含视图层的任何类或者结构体
绑定是一种响应式的通信方式。当被绑定对象某个值的变化时,绑定对象会自动感知,无需被绑定对象主动通知绑定对象。可以使用KVO和RAC实现。例如在Label中显示倒计时,是V绑定了包含定时器的VM。
双向绑定在MVVM中指的是V和VM之间相互绑定。例如TextField的text长度达到阈值,另一个Button改变背景颜色。这个过程中首先VM感知V中TextField的text属性长度变化,V感知VM中对应的状态属性。一旦V中TextField的text属性长度超出VM中的阈值,VM中的状态属性改变,触发V中Button的背景色发生改变。
首先,这三种模式都是由Model层、View层以及中间层(C/P/VM)构成,这三者的模型层没有太大区别,从理论来说都是数据来源;视图层在理论上都被设计为被动,但在实际上略有不同。在实际开发中,MVC中的View和Controller高度耦合,几乎所有的操作都统一由ViewController管理,但是从理论来说MVC希望视图层就是单纯的View/ViewController,只需要负责UI的更新和交互,不涉及业务逻辑和模型更新。在实际开发中MVP以及MVVM实现了MVC的理论期望,即使得视图和中间层分离。但是MVP的视图层是完全被动的,单纯的吧交互传递给中间层;而MVVM的视图层并不是完全被动的,他可以监视中间层的变化,一旦产生变化可以自动进行相应的变化。
针对于中间层的设计是三种架构的核心差异。从逻辑上来说,中间层的左右就是链接视图层和模型层,用于处理交互、接受通知和完成数据更新。对于MVC的中间层会持有V和M,主要起到组装和连接的作用,通过传递参数和实例变量来完成所有的操作。MVP的Presenter中间层持有M,在更新Model上与MVC的Controller作用一致,但是他不持有V,相反V持有中间层Presenter。中间层的工作流程即:从V接受交互传递--->响应--->向V传递响应指令--->V进行更新。这些全部的操作都需要手动书写代码完成。MVVM的中间层ViewModel持有M,在更新模型上与以上两种相同。它完全独立于视图层,V拥有中间层ViewModel,通过绑定属性自动进行更新。全部操作由响应式逻辑框架自动完成。
总的来说MVC耦合度比较高,代码分配不合理,维护和扩展成本比较高,但是不需要进行层级传递,代码总量比较少,便于理解和应用。MVP和MVVM相似,耦合度和代码分配比较合理,便于测试,但是MVP的视图层需要吧所有的交互传递给中间层,且需要手动实现响应和更新,代码总量远超MVVM。MVVM在响应和更新上,通过响应式框架自动进行操作,精简了代码量,但是需要引入第三方响应式框架,同时因为属性观察环环相扣,调用栈比较大,不便于debug。
有兴趣的还可以参考这片博客 iOS架构模式MVC、MVP、MVVM(内附demo)
更加具体的可以参考这片博客 iOS 分析MVC、MVP、MVVM、VIPER