進行 iOS 開發時,幾乎都曾聽過這個笑話。
「MVC 不是 Model-View-Controller,而是 Massive View Controller 的縮寫。」
雖然是玩笑話,卻一針見血。明明是 Apple 官方推薦的架構,為什麼照著做,View Controller 最後會變成數千行的怪物?
作為 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等View 生命週期 - 處理畫面旋轉、版面配置更新等與 View 緊密相關的事件
- 實作
UITableViewDataSource、UITableViewDelegate等View 協定
到這裡都還算是 View 相關工作。問題在於,「那網路請求要寫在哪裡?」「畫面切換呢?」「資料格式化呢?」這些問題沒有明確答案。它們既不是 Model,也不是 View,最後就全部塞進 View Controller。
肥大的 View Controller 典型長相
常見的 View Controller 結構,用程式碼概括大致如下。
final class ProfileViewController: UIViewController {
// 1. View 屬性
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,也要有意識地拆出可以從 View Controller 移除的責任。
- 網路與資料邏輯 → 獨立的服務物件
- 畫面切換 → Coordinator 等專責物件
- Cell 建構、格式化 → 專用型別
接著你會想進一步拆出「建立畫面要顯示之狀態的邏輯」。答案就是下一篇要介紹的MVVM。接下來也會整理為什麼需要 ViewModel,以及沒有繫結時為什麼只完成了一半。
總結
- MVC 本身沒有錯。問題在於 iOS 的結構特性:UIViewController 同時扮演 View 與 Controller。
- Model 和 View 都不是的程式碼無處可去,最後全堆在 View Controller 裡,這就是 Massive View Controller 的真面目。
- 對小型畫面來說,MVC 仍然有效。不過網路、畫面切換與格式化必須有意識地分離。
- 若要連建立 View 狀態的邏輯也分離,就需要 MVVM。下一篇再談。

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