一、概述

在 iOS 开发中,MVC(Model View Controller)是构建iOS App的标准模式,是苹果推荐的一个用来组织代码的权威范式。Apple甚至是这么说的。在MVC下,所有的对象被归类为一个Model,一个View,和一个Controller。Model持有数据,View显示与用户交互的界面,而ViewController调解Model和View之间的交互。现在,MVC 依然是目前主流客户端编程框架,但同时它也被调侃成Massive View Controller(重量级视图控制器),想必开发者在开发中无可避免被下面几个问题所困扰:

  • 厚重的ViewController

  • 遗失的网络逻辑(无立足之地)

  • 较差的可测试性

为了避免和解决上述问题的产生,从MVC引申出来一种维护性较强、耦合性低的新的架构MVVM(Model View View-Mode),MVVM正式规范了视图和控制器紧耦合的性质,并引入新的组件。MVVM主要目的是为了分离视图(View)和模型(Model)。

本文只是分享一下笔者对MVC和MVVM的一些见解,在此抛砖引玉,希望能为存在对MVC和MVVM迷茫的广大开发者提供一点思路,少走一些弯路,填补一些细坑。文章仅供大家参考,若有不妥之处,还望不吝赐教,欢迎批评指正。

二、MVC(Model View Controller)

1.MVC之间的关系
任何一个正经开发过软件的人都熟悉MVC,它意思是Model View Controller, 是一个在复杂应用设计中组织代码的公认模式,它们之间的结构关系如下:

我们看到的只是一个苹果 典型的MVC 设置。view将用户交互通知给controller。view controller通过更新model来反应状态的改变。model(通常使用Key-Value-Observation)通知controller来更新他们负责的view。大多数iOS应用程序的代码使用这种方式来组织。然而,典型的MVC架构不适用于当下的iOS开发。尽管从技术上看View 和 Controller 是相互独立的,但事实上它们几乎总是结对出现,一个 View 只能与一个 Controller 进行匹配,反之亦然。既然如此,那我们为何不正规化它们的连接:

因此,M-VC 可能是对 iOS 开发中的 MVC模式更为准确的解读,同时更也准确地描述了我们日常开发可能已经编写的 MVC 代码,但它并没有做太多事情来解决 iOS 应用中日益增长的重量级视图控制器的问题。(PS:躺枪了没...)

举例说明:

若假设笔者利用MVC的设计模式来开发此界面,那想必是这样的。

  • M:SUGoods(商品模型Model)

  • V:SUGoodsCell(展示商品数据的View,自定义的  UITableViewCell)

  • C:SUHomeViewController (首页控制器Controller)

控制器(SUHomeViewController)代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void) requestRemoteData
{
   // 1.发起网络请求,获取到服务器的数据,并将其转化成模型数据(`SUGoods`)
   // 2.添加到数据源(`dataSource`)
   // 3.最后刷新表格`[self.tableView reloadData]`,配置`SUGoodsCel`l即可
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    SUGoodsCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Goods"];
    cell.goods = self.dataSource[indexPath.row];
    return cell;
}

View(SUGoodsCell)代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SUGoodsCell.h
@class SUGoods;
@interface SUGoodsCell : UITableViewCell
@property (nonatomic, strong) SUGoods *goods;
@end
 
SUGoodsCell.m
@implementation SUGoodsCell
- (void)setGoods:(SUGoods *)goods
{
     _goods = goods
     /// config data ....
}
@end

都写到这份上了,大家用脚趾头想想,这个SUGoodsCell,正是由View直接来调用Model,所以事实上典型的MVC的原则已经违背了,但是这种情况是一直发生的甚至于人们不觉得这里有哪些不对。如果严格遵守MVC的话,你会把对cell的设置放在Controller中,不向View传递一个Model对象,这样就会大大增加Controller的体积。所以说,这哪里是典型的MVC设计模式,这分明是M-VC设计模式呀。简而言之,在理想的世界里,MVC也许工作的很好。然而,我们生活在真实的世界,谢谢(PS:让梦想实现的最好的方式,就是醒来!!!)。

2.MVC的弊端
MVC的利弊大家想必是有目共睹的,Massive View Controller的说法也并非空穴来风的。让我们一起探讨MVC的弊端,剖析问题产生原因,打造一个轻量级的ViewController,明确MVC设计模式中各个角色的职责。

厚重的View Controller

  • M:模型model的对象通常非常的简单。根据Apple的文档,model应包括数据和操作数据的业务逻辑。而在实践中,model层往往非常薄,不管怎样,model层的业务逻辑不应被拖入到controller。
  • V:视图view通常是UIKit控件(component,这里根据习惯译为控件)或者编码定义的UIKit控件的集合。View的如何构建(PS:IB或者手写界面)何必让Controller知晓,同时View不应该直接引用model(PS:现实中,你懂的!),并且仅仅通过IBAction事件引用controller。业务逻辑很明显不归入view,视图本身没有任何业务。
  • C:控制器controller。Controller是app的“胶水代码”:协调模型和视图之间的所有交互。控制器负责管理他们所拥有的视图的视图层次结构,还要响应视图的loading、appearing、disappearing等等,同时往往也会充满我们不愿暴露的model的模型逻辑以及不愿暴露给视图的业务逻辑。

网络数据的请求及后续处理,本地数据库操作,以及一些带有工具性质辅助方法都加大了Massive View Controller的产生。

  • 遗失(无处安放)的网络逻辑

苹果使用的MVC的定义是这么说的:所有的对象都可以被归类为一个model,一个view,或是一个controller。

你可能试着把它放在Model对象里,但是也会很棘手,因为网络调用应该使用异步,这样如果一个网络请求比持有它的model生命周期更长,事情将变的复杂。显然View里面做网络请求那就更格格不入了,因此只剩下Controller了。若这样,这又加剧了Massive View Controller的问题。若不这样,何处才是网络逻辑的家呢?

  • 较差的可测试性

由于View Controller混合了视图处理逻辑和业务逻辑,分离这些成分的单元测试成了一个艰巨的任务。若一个Massive View Controller有上万行代码,要你编写个单元测试,我敢保证,你不是想写,你是想死,分分钟填表走人。

三、MVVM(Model View View-Model)

一种可以很好地解决Massive View Controller问题的办法就是将 Controller 中的展示逻辑抽取出来,放置到一个专门的地方,而这个地方就是 viewModel 。MVVM衍生于MVC,是对 MVC 的一种演进,它促进了 UI 代码与业务逻辑的分离。它正式规范了视图和控制器紧耦合的性质,并引入新的组件。他们之间的结构关系如下:

MVVM 的基本概念

  • 在MVVM 中,view 和 view controller正式联系在一起,我们把它们视为一个组件
  • view 和 view controller 都不能直接引用model,而是引用视图模型(viewModel)
  • viewModel 是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他代码的地方
  • 使用MVVM会轻微的增加代码量,但总体上减少了代码的复杂性

MVVM 的注意事项

  • view 引用viewModel ,但反过来不行(即不要在viewModel中引入#import UIKit.h,任何视图本身的引用都不应该放在viewModel中)(PS:基本要求,必须满足)
  • viewModel 引用model,但反过来不行

MVVM 的使用建议

  • MVVM 可以兼容你当下使用的MVC架构。
  • MVVM 增加你的应用的可测试性。
  • MVVM 配合一个绑定机制效果最好(PS:ReactiveCocoa你值得拥有)。
  • viewController 尽量不涉及业务逻辑,让 viewModel 去做这些事情。
  • viewController 只是一个中间人,接收 view 的事件、调用 viewModel 的方法、响应 viewModel 的变化。
  • viewModel 绝对不能包含视图 view(UIKit.h),不然就跟 view 产生了耦合,不方便复用和测试。
  • viewModel之间可以有依赖。
  • viewModel避免过于臃肿,否则重蹈Controller的覆辙,变得难以维护。

MVVM 的优势

  • 低耦合:View 可以独立于Model变化和修改,一个 viewModel 可以绑定到不同的 View 上
  • 可重用性:可以把一些视图逻辑放在一个 viewModel里面,让很多 view 重用这段视图逻辑
  • 独立开发:开发人员可以专注于业务逻辑和数据的开发 viewModel,设计人员可以专注于页面设计
  • 可测试:通常界面是比较难于测试的,而 MVVM 模式可以针对 viewModel来进行测试

MVVM 的弊端

  • 数据绑定使得Bug 很难被调试。你看到界面异常了,有可能是你 View 的代码有 Bug,也可能是 Model 的代码有问题。数据绑定使得一个位置的 Bug 被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
  • 对于过大的项目,数据绑定和数据转化需要花费更多的内存(成本)。

主要成本在于:

  • 数组内容的转化成本较高:数组里面每项都要转化成Item对象,如果Item对象中还有类似数组,就很头疼。
  • 转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需要第二次转化。
  • 只有在API返回的数据高度标准化时,这些对象原型(Item)的可复用程度才高,否则容易出现类型爆炸,提高维护成本。
  • 调试时通过对象原型查看数据内容不如直接通过NSDictionary/NSArray直观。
  • 同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方。

四、总结

MVC的设计模式也并非是病入膏肓,无药可救的架构,最起码目前MVC设计模式仍旧是iOS开发的主流框架,存在即合理。针对文章所述的弊端,我们依旧有许多可行的方法去避免和解决,从而打造一个轻量级的ViewController。

MVVM是MVC的升级版,完全兼容当前的MVC架构,MVVM虽然促进了UI 代码与业务逻辑的分离,一定程度上减轻了ViewController的臃肿度,但是View和ViewModel之间的数据绑定使得 MVVM变得复杂和难用了,如果我们不能更好的驾驭两者之间的数据绑定,同样会造成Controller 代码过于复杂,代码逻辑不易维护的问题。

一个轻量级的ViewController是基于MVC和MVVM模式进行代码职责的分离而打造的。MVC和MVVM有优点也有缺点,但缺点在他们所带来的好处面前时不值一提的。他们的低耦合性,封装性,可测试性,可维护性和多人协作便利大大提高了开法效率。

同时,我们需要保持的是一个拥抱变化的心,以及理性分析的态度。在新技术的面前,不盲从,也不守旧,一切的决策都应该建立在认真分析的基础上,这样才能应对技术的变化。

iOS 关于MVC和MVVM设计模式的那些事的更多相关文章

  1. (一)mvc与mvvm设计模式

    前沿:了解设计模式对我们而言,具有很大意义,对语言没有限制,它适用于任何语言,是一种变成思想.设计模式最初有四人帮提出,有兴趣的同学可以去了解下,今天给大家主要分析mvc与mvvm设计模式 一.mvc ...

  2. MVC和MVVM设计模式简单理解

    1.mvc设计模式理解 Model: 模型 持有所有的数据状态和业务逻辑; 泛指数据库,链接数据库,建立数据模型 View: 视图 用来展示数据模型在页面上,泛指前端 Controller: 控制器, ...

  3. IOS的MVC和MVVM模式简明介绍

    iOS中的MVC(Model-View-Controller)将软件系统分为Model.View.Controller三部分,结构图如下: Model: 你的应用本质上是什么(但不是它的展示方式) C ...

  4. MVC与MVVM设计模式理解

    MVC设计模式(View和Model之间不能直接通信) MVC是一种架构模式,M表示Model,V表示视图View,C表示控制器Controller: Model负责存储.定义.操作数据.从网络中获取 ...

  5. iOS开发项目之MVC与MVVM

    MVC MVC,Model-View-Controller,我们从这个古老而经典的设计模式入手.采用 MVC 这个架构的最大的优点在于其概念简单,易于理解,几乎任何一个程序员都会有所了解,几乎每一所计 ...

  6. iOS——MVVM设计模式

    一.典型的iOS构架——MVC 在典型的MVC设置中,Model呈现数据,Vie呈现用户界面,而ViewController调节它两者之间的交互. 虽然View和View Controller是技术上 ...

  7. iOS中MVC设计模式

    在组织大型项目的代码文件时,我们常用MVC的思想.MVC的概念讲起来非常简单,就和对象(object)一样.但是理解和应用起来却非常困难.今天我们就简单总结一下MVC设计理念. MVC(Model V ...

  8. iOS中MVC等设计模式详解

    iOS中MVC等设计模式详解 在iOS编程,利用设计模式可以大大提高你的开发效率,虽然在编写代码之初你需要花费较大时间把各种业务逻辑封装起来.(事实证明这是值得的!) 模型-视图-控制器(MVC)设计 ...

  9. iOS 基于MVC设计模式的基类设计

    iOS 基于MVC设计模式的基类设计 https://www.jianshu.com/p/3b580ffdae00

随机推荐

  1. 20170607 JDBC课堂实践 任务四

    20170607 JDBC课堂实践 任务四 题目 查询world数据库,查询哪个国家的平均寿命最长. 码云链接 SQL语句 SELECT Code, Name, LifeExpectancy FROM ...

  2. STM32运行FreeRTOS出现prvTaskExitError错误死机

    文件port.c prvTaskExitError();任务退出错误,一个可能在任务里面写了return,另一个可能任务切换退出问题,入栈和出栈的时候出了问题. static void prvTask ...

  3. CentOS7安装及配置vsftpd (FTP服务器)

    CentOS7安装及配置vsftpd (FTP服务器) 1.安装vsftpd 1 yum -y install vsftpd 2.设置开机启动 1 systemctl enable vsftpd 3. ...

  4. 通知的多线程问题 iOS

    发送通知在子线程,接受也在子线程.如果子线程操作UI,会打印一推日志,告诉我们应该主线程操作.

  5. 通过dotnet命令行设置asp.net core服务的启动地址

    需求: 通过dotnet命令行启动asp.net core 服务时,自定义监听端口. 方法: 在program.cs中增加命令行参数配置: WebHost.CreateDefaultBuilder(a ...

  6. javaweb(七)——HttpServletResponse对象(一)

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象.request和response对象即然代表请求和响应,那我们要 ...

  7. AtCoder Grand Contest 026 D - Histogram Coloring

    一列中有两个连续的元素,那么下一列只能选择选择正好相反的填色方案(因为连续的地方填色方案已经确定,其他地方也就确定了) 我们现将高度进行离散化到Has数组中,然后定义dp数组 dp[i][j] 表示前 ...

  8. cmake-index-3.11.4机翻

    index next | CMake » git-stage git-master latest release 3.13 3.12 3.11.4 3.10 3.9 3.8 3.7 3.6 3.5 3 ...

  9. Paper Reading - Deep Captioning with Multimodal Recurrent Neural Networks ( m-RNN ) ( ICLR 2015 ) ★

    Link of the Paper: https://arxiv.org/pdf/1412.6632.pdf Main Points: The authors propose a multimodal ...

  10. 算法笔记(c++)--关于01背包的滚动数组

    算法笔记(c++)--关于01背包的滚动数组 关于01背包问题:基本方法我这篇写过了. https://www.cnblogs.com/DJC-BLOG/p/9416799.html 但是这里数组是N ...