6-1.【架构设计】MVC 在 iOS 中“失控”的根本原因是什么?如何从职责划分和生命周期角度解释?

4 阅读3分钟

一、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 不能

    • 持有业务状态
    • 决定流程
    • 协调多个子视图
  • 它只能:

    • draw
    • layout
    • 抛事件

于是所有 “非渲染但又不算纯数据” 的东西都只能放哪?

👉 ViewController


3️⃣ Model 在 UIKit MVC 中缺位

UIKit 从来没给你一个官方的 Model 层规范

  • Model 没有:

    • 生命周期
    • 依赖注入
    • 状态同步机制
  • 所以你经常看到:

    class UserViewController {
        var user: User?
        var users: [User] = []
    }
    

👉 Model 退化成了 struct / DTO
👉 业务规则自然跑进 VC


4️⃣ 导航与协调职责只能在 VC

  • pushViewController
  • present
  • dismiss

这些 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 中“解放”出来