一、Swift / iOS 里 MVC“失控”的根本原因(一句话版)
UIKit 的 MVC 是“偏置的 MVC”:View Controller 同时承担了 View 和 Controller 的职责,又被绑定到复杂且不可替代的生命周期,导致业务逻辑、展示逻辑、协调逻辑全部向它坍缩。
展开说就是三个词:
权力过大 + 生命周期太重 + 缺乏天然边界
二、从「职责划分」角度看:为什么 ViewController 会失控?
1️⃣ 理论 MVC vs UIKit MVC 的本质差异
经典 MVC(Smalltalk / Web MVC)
| 角色 | 职责 |
|---|---|
| Model | 纯业务状态 & 规则 |
| View | 纯渲染 |
| Controller | 输入转换 / 流程协调 |
UIKit MVC 实际长这样
UIViewController
├── 管 View(创建、布局、更新)
├── 管用户交互(按钮、手势)
├── 管数据获取(API / DB)
├── 管状态机(loading / error / empty)
├── 管导航(push / present)
├── 管业务规则(if else 写满)
👉 Controller 不再是“胶水”,而是“垃圾回收站”
2️⃣ View 在 iOS 中是“被动的、哑的”
-
UIView不能- 持有业务状态
- 决定流程
- 协调多个子视图
-
它只能:
drawlayout- 抛事件
于是所有 “非渲染但又不算纯数据” 的东西都只能放哪?
👉 ViewController
3️⃣ Model 在 UIKit MVC 中缺位
UIKit 从来没给你一个官方的 Model 层规范
-
Model 没有:
- 生命周期
- 依赖注入
- 状态同步机制
-
所以你经常看到:
class UserViewController { var user: User? var users: [User] = [] }
👉 Model 退化成了 struct / DTO
👉 业务规则自然跑进 VC
4️⃣ 导航与协调职责只能在 VC
pushViewControllerpresentdismiss
这些 API 只能由 ViewController 调用
这意味着:
- 页面切换逻辑
- 模块间通信
- 流程控制
👉 100% 堆进 ViewController
三、从「生命周期」角度看:为什么它注定会膨胀?
1️⃣ ViewController 生命周期 = iOS 应用的“主脉搏”
init
↓
viewDidLoad
↓
viewWillAppear
↓
viewDidAppear
↓
viewWillDisappear
↓
deinit
关键问题是:
几乎所有重要事件,都会“合理地”发生在 VC 生命周期里
例如:
| 事情 | 常见放置位置 |
|---|---|
| 请求数据 | viewDidLoad / viewWillAppear |
| 订阅通知 | viewWillAppear |
| 解绑资源 | viewWillDisappear |
| UI 刷新 | viewWillAppear |
| 动画 / 埋点 | viewDidAppear |
👉 生命周期钩子太“方便”,导致它成了默认落点
2️⃣ VC 生命周期不可替代、不可拆分
-
你 不能
- 自定义一个同等级生命周期对象
- 把 VC 生命周期“委托”出去(没有官方机制)
-
你 必须
- 在 VC 里响应系统事件
- 在 VC 里接收回调
结果是:
即使你想“拆职责”,生命周期依赖也会把代码拉回 VC
3️⃣ UIKit 是“以 VC 为中心”的框架
UIKit 的设计假设是:
一个屏幕 = 一个 ViewController
这直接导致:
- 所有“屏幕级别”的状态
- 所有“页面级别”的逻辑
- 所有“页面级别”的依赖
👉 天然挂在 VC 上
四、总结一下:MVC 为什么在 iOS 中必然走向失控?
从根因层面看,其实是三件事叠加:
🧨 1. Controller 权责无限
- View 太弱
- Model 太薄
- Controller 成为唯一可扩展点
🧨 2. 生命周期强绑定
- 业务逻辑天然依赖 VC 生命周期
- 无法优雅地外移
🧨 3. 框架设计倾向
- UIKit API 把你往 VC 推
- 导航、事件、系统回调只能在 VC
五、一句“本质总结”(架构层面)
Swift / UIKit 中的 MVC 不是“职责分离失败”,而是“职责根本无法被分离”。
这也是为什么:
-
Massive View Controller 成了 iOS 的“集体记忆”
-
MVVM / VIPER / TCA / Clean Architecture 本质都是在:
- 把职责
- 把生命周期
- 把流程控制
从 ViewController 中“解放”出来