iOS 工程

[iOS 架構 #1] iOS MVC 模式與 Massive View Controller 產生的真正原因

進行 iOS 開發時,幾乎都曾聽過這個笑話。

閱讀 4 分鐘
[iOS 架構 #1] iOS MVC 模式與 Massive View Controller 產生的真正原因 封面圖

進行 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 會負責以下所有工作。

  • 管理viewDidLoadviewWillAppearView 生命週期
  • 處理畫面旋轉、版面配置更新等與 View 緊密相關的事件
  • 實作UITableViewDataSourceUITableViewDelegateView 協定

到這裡都還算是 View 相關工作。問題在於,「那網路請求要寫在哪裡?」「畫面切換呢?」「資料格式化呢?」這些問題沒有明確答案。它們既不是 Model,也不是 View,最後就全部塞進 View Controller。

光是名稱就是 View + Controller,預設就是一個人包辦所有事情。
光是名稱就是 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。下一篇再談。

延伸閱讀