进行iOS开发时,几乎每个人都听过这个笑话。
“MVC不是Model-View-Controller,而是Massive View Controller的缩写。”
这是句玩笑话,但很有道理。明明是Apple官方推荐的架构,为什么照着做,视图控制器最终会变成几千行的怪物?
作为iOS架构系列的第一篇,今天我们来梳理MVC原本的样子,以及为什么iOS无法按那个样子工作。
先说结论,问题不在MVC概念本身,而在于UIViewController横跨View和Controller边界的结构。
MVC原本是什么样的结构?
MVC是1979年诞生于Smalltalk的古老模式。原始形式明确划分了三种职责。
- Model:数据和业务逻辑
- View:屏幕显示
- Controller:接收用户输入并传递给Model
在原始MVC中,View直接观察Model。Model发生变化时,View会自动更新。
不过Apple的MVC有所不同。它让View和Model彼此完全不知情,并改为由Controller在中间负责所有协调。这是为了提高复用性的合理选择,却也为Controller承担过多工作埋下了伏笔。
UIViewController光是名字就违规了
Apple MVC中的Controller在iOS里由UIViewController实现。但请再看看这个类名:View + Controller。名字本身就合并了两种职责。
实际上,UIViewController负责以下所有工作。
- 管理
viewDidLoad、viewWillAppear等视图生命周期 - 处理屏幕旋转、布局更新等与视图紧密相关的事件
- 实现
UITableViewDataSource、UITableViewDelegate等视图协议
到这里还都是视图相关工作。问题是,像“网络请求写在哪里?”“页面跳转呢?”“数据格式化呢?”这些问题都没有明确答案。它们既不是Model,也不是View,最后全都进了视图控制器。
臃肿视图控制器的典型样子
常见的视图控制器结构,用代码概括如下。
final class ProfileViewController: UIViewController {
// 1. 视图属性
private let tableView = UITableView()
// 2. 状态(实际上是 Model 缓存)
private var user: User?
private var posts: [Post] = []
override func viewDidLoad() {
super.viewDidLoad()
setupLayout() // 3. 布局代码
fetchProfile() // 4. 网络请求
}
private func fetchProfile() {
URLSession.shared.dataTask(...) { ... } // 5. 解析、错误处理
}
@objc private func editTapped() {
// 6. 甚至直接负责页面跳转
navigationController?.pushViewController(EditViewController(), animated: true)
}
}
布局、状态管理、网络、解析和页面跳转全都在一个文件里。再加上delegate实现,很快就会超过一千行。
这种结构真正的代价不是代码行数,而是无法进行测试。即使要验证“user为nil时隐藏编辑按钮”这样的简单逻辑,也必须完整启动UIViewController并模拟它的生命周期。
那么,MVC应该放弃吗?
不一定。对于小型页面,MVC仍然是最快、最简单的选择。Apple的框架本身就是以MVC为前提设计的,强行套用其他结构反而可能增加摩擦。
关键在于,即使使用MVC,也要有意识地拆出可以从视图控制器中移除的职责。
- 网络与数据逻辑 → 独立的服务对象
- 页面跳转 → Coordinator等专用对象
- 单元格构建、格式化 → 专用类型
接下来,你会想把“创建屏幕要显示的状态”的逻辑也分离出去。答案就是下一篇要介绍的MVVM。我们还会梳理为什么需要ViewModel,以及没有绑定时为什么只能完成一半。
总结
- MVC本身没有错。问题在于iOS的结构特性:UIViewController同时承担View和Controller的角色。
- 既不是Model也不是View的代码无处安放,最终全都堆积到视图控制器中,这就是Massive View Controller的本质。
- 对于小型页面,MVC仍然有效。但网络、页面跳转和格式化必须有意识地分离。
- 如果还要分离创建视图状态的逻辑,就需要MVVM。下一篇介绍。

![[iOS架构 #1] iOS MVC模式与Massive View Controller产生的真正原因 封面图](/assets/images/posts/a3101292-21b1-4a89-b770-3e844f1a23c6/1.jpg)