iOS開發架構——MVC、MVP和MVVM對比

文章目錄

  • 前言
  • MVC(Model - View - Controller)
  • MVP(Model - View - Presenter)
  • MVVM(Model - View - ViewModel)


前言

在 iOS 開發中,MVC、MVVM、和 MVP 是常見的三種架構模式,它們主要目的是解耦視圖與業務邏輯,提高代碼復用性和可維護性。下面我將通過一個簡單的示例「展示用戶名」來解釋它們的區別,并配上對應代碼。

假設場景:

  • 有一個 User 模型,包含昵稱 name
  • UI 包含一個 UILabel 顯示名字,一個 UIButton 模擬點擊“加載用戶”
  • 點擊按鈕 → 加載用戶數據 → 顯示用戶昵稱

MVC(Model - View - Controller)

特點:

  • Controller 是橋梁:連接 Model 和 View
  • View 很「傻」,Controller 很「胖」(邏輯全堆里面)
// Model
struct User {var name: String
}// ViewController 充當 Controller 和 View 的職責
class MVCViewController: UIViewController {let nameLabel = UILabel()let loadButton = UIButton(type: .system)override func viewDidLoad() {super.viewDidLoad()setupUI()}func setupUI() {nameLabel.frame = CGRect(x: 50, y: 100, width: 200, height: 30)loadButton.frame = CGRect(x: 50, y: 150, width: 100, height: 40)loadButton.setTitle("加載用戶", for: .normal)loadButton.addTarget(self, action: #selector(loadUser), for: .touchUpInside)view.addSubview(nameLabel)view.addSubview(loadButton)}// 模擬點擊:在 Controller 中處理邏輯@objc func loadUser() {let user = User(name: "小明") // 模擬從后端獲取nameLabel.text = user.name    // 更新 UI}
}

在 iOS 的 MVC 架構中,ViewController 集中包含了 View 和 Controller 的邏輯,這是由 Apple 官方 UIKit 框架的設計風格所造成的,而不是 MVC 理論的本意。

UIKit 組件(如 UIViewController)本身就是既負責控制邏輯(Controller),又默認持有和管理 UI(View)的類。比如:

class MyViewController: UIViewController {override func viewDidLoad() {super.viewDidLoad()// 控制邏輯(Controller)// 創建和管理 UI(View)let label = UILabel()label.text = "Hello"self.view.addSubview(label)}
}

UIViewController.view 就是一個默認的視圖容器。所以,View 和 Controller 就混在一起用了。

所以很多人吐槽 iOS MVC 是 “Massive View Controller”,代碼都堆在 VC 里,不利于維護。

MVP(Model - View - Presenter)

在 iOS 的 MVC 中,ViewController 既負責 UI,又負責業務邏輯。隨著功能增加,代碼會變得:

  • 臃腫(Massive ViewController)
  • 難以測試
  • 難以復用

MVP 將業務邏輯(如獲取數據、處理點擊事件)抽到 Presenter 中,ViewController 只關心 UI。

特點:

  • Presenter 處理業務邏輯(可以單元測試),不依賴 UIKit
  • View 是一個協議,由 ViewController 實現(View 使用協議抽象,可以 mock View,可以單元測試)
  • 更適合做單元測試
// Model
struct User {var name: String
}// View 協議:只關心顯示邏輯
protocol UserView: AnyObject {func displayUserName(_ name: String)
}// Presenter:負責處理業務邏輯
class UserPresenter {weak var view: UserView?init(view: UserView) {self.view = view}func fetchUser() {let user = User(name: "小紅") // 模擬數據獲取view?.displayUserName(user.name)}
}// ViewController 實現 View 協議
class MVPViewController: UIViewController, UserView {let nameLabel = UILabel()let loadButton = UIButton(type: .system)var presenter: UserPresenter!override func viewDidLoad() {super.viewDidLoad()presenter = UserPresenter(view: self)setupUI()}func setupUI() {nameLabel.frame = CGRect(x: 50, y: 100, width: 200, height: 30)loadButton.frame = CGRect(x: 50, y: 150, width: 100, height: 40)loadButton.setTitle("加載用戶", for: .normal)loadButton.addTarget(self, action: #selector(loadUserTapped), for: .touchUpInside)view.addSubview(nameLabel)view.addSubview(loadButton)}@objc func loadUserTapped() {presenter.fetchUser()}func displayUserName(_ name: String) {nameLabel.text = name}
}

雖然 MVP 已經將業務邏輯抽離到了 Presenter,但:

  1. View 仍需手動更新 UI,Presenter 獲取數據后,需要調用 View 協議來“告訴”它更新 UI:
// ViewController 實現這些更新方法,很容易隨著頁面變復雜而變臃腫。
view?.showUserName(user.name)
  1. 通信是被動式的,如果狀態很多(例如 name、age、頭像等),Presenter 需要逐個通知,View 也要逐個處理。

MVVM(Model - View - ViewModel)

特點:

  • 引入綁定機制(數據驅動 UI)
MVVM 中,ViewModel 暴露可觀察屬性(如 Swift 的 @PublishedRxSwiftObservableUI 層通過綁定,一旦數據變化就自動刷新,不需要手動調用更新方法。
  • ViewController 更輕量,不再關心如何顯示,而是“綁定好”UI 到 ViewModel
viewModel.$name.sink { self.nameLabel.text = $0 }
  • 狀態管理更統一,ViewModel 通常以“狀態集合”的方式存在,能更好地組織
@Published var isLoading: Bool
@Published var name: String
@Published var errorMessage: String?
import Combine// Model
struct User {var name: String
}// ViewModel:處理數據邏輯和 UI 映射
class UserViewModel: ObservableObject {@Published var username: String = ""func loadUser() {let user = User(name: "小花")username = user.name // 觸發 UI 更新}
}// ViewController
class MVVMViewController: UIViewController {let nameLabel = UILabel()let loadButton = UIButton(type: .system)var viewModel = UserViewModel()var cancellables = Set<AnyCancellable>()override func viewDidLoad() {super.viewDidLoad()setupUI()bindViewModel()}func setupUI() {nameLabel.frame = CGRect(x: 50, y: 100, width: 200, height: 30)loadButton.frame = CGRect(x: 50, y: 150, width: 100, height: 40)loadButton.setTitle("加載用戶", for: .normal)loadButton.addTarget(self, action: #selector(loadUserTapped), for: .touchUpInside)view.addSubview(nameLabel)view.addSubview(loadButton)}func bindViewModel() {viewModel.$username.receive(on: RunLoop.main).sink { [weak self] name inself?.nameLabel.text = name}.store(in: &cancellables)}@objc func loadUserTapped() {viewModel.loadUser()}
}

分析:

  • 數據綁定調試困難(數據變化自動觸發 UI,數據流是隱式的,調試時很難知道“誰更新了誰”)
  • 不適合所有頁面(小頁面 + 簡單邏輯,用 MVC 或 MVP 更高效)
  • 雙向綁定濫用風險(狀態混亂或循環更新)
  • ViewModel 可能過于龐大(Massive ViewModel)

ViewModel 承擔了很多職責,如果不合理拆分,容易臃腫,等同于從 ViewController 搬家而已。

  • 接收用戶輸入
  • 做業務邏輯
  • 暴露 UI 狀態
  • 響應用戶行為

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/78721.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/78721.shtml
英文地址,請注明出處:http://en.pswp.cn/web/78721.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

0506--01-DA

36. 單選題 在娛樂方式多元化的今天&#xff0c;“ ”是不少人&#xff08;特別是中青年群體&#xff09;對待戲曲的態度。這里面固然存在 的偏見、難以靜下心來欣賞戲曲之美等因素&#xff0c;卻也有另一個無法回避的原因&#xff1a;一些戲曲雖然與觀眾…

關于Java多態簡單講解

面向對象程序設計有三大特征&#xff0c;分別是封裝&#xff0c;繼承和多態。 這三大特性相輔相成&#xff0c;可以使程序員更容易用編程語言描述現實對象。 其中多態 多態是方法的多態&#xff0c;是通過子類通過對父類的重寫&#xff0c;實現不同子類對同一方法有不同的實現…

【Trea】Trea國際版|海外版下載

Trea目前有兩個版本&#xff0c;海外版和國內版。? Trae 版本差異 ?大模型選擇?&#xff1a; ?國內版?&#xff1a;提供了字節自己的Doubao-1.5-pro以及DeepSeek的V3版本和R1版本。海外版&#xff1a;提供了ChartGPT以及Claude-3.5-Sonnet和3.7-Sonnt. ?功能和界面?&a…

Missashe考研日記-day33

Missashe考研日記-day33 1 專業課408 學習時間&#xff1a;2h30min學習內容&#xff1a; 今天開始學習OS最后一章I/O管理的內容&#xff0c;聽了第一小節的內容&#xff0c;然后把課后習題也做了。知識點回顧&#xff1a; 1.I/O設備分類&#xff1a;按信息交換單位、按設備傳…

鏈表的面試題3找出中間節點

來來來&#xff0c;接著繼續我們的第三道題 。 解法 暴力求解 快慢指針 https://leetcode.cn/problems/middle-of-the-linked-list/submissions/ 這道題的話&#xff0c;思路是非常明確的&#xff0c;就是讓你找出我們這個所謂的中間節點并且輸出。 那這道題我們就需要注意…

linux磁盤介紹與LVM管理

一、磁盤基本概述 GPT是全局唯一標識分區表的縮寫,是全局唯一標示磁盤分區表格式。而MBR則是另一種磁盤分區形式,它是主引導記錄的縮寫。相比之下,MBR比GPT出現得要更早一些。 MBR 與 GPT MBR 支持的磁盤最大容量為 2 TB,GPT 最大支持的磁盤容量為 18 EB,當前數據盤支持…

突破測試環境文件上傳帶寬瓶頸!React Native 阿里云 OSS 直傳文件格式問題攻克二

上一篇我們對服務端和阿里云oss的配置及前端調用做了簡單的介紹&#xff0c;但是一直報錯。最終判斷是文件格式問題&#xff0c;通常我們在reactnative中用formData上傳&#xff0c; formData.append(file, {uri: file, name: nameType(type), type: multipart/form-data});這…

Spring Boot 中 @Bean 注解詳解:從入門到實踐

在 Spring Boot 開發中&#xff0c;Bean注解是一個非常重要且常用的注解&#xff0c;它能夠幫助開發者輕松地將 Java 對象納入 Spring 容器的管理之下&#xff0c;實現對象的依賴注入和生命周期管理。對于新手來說&#xff0c;理解并掌握Bean注解&#xff0c;是深入學習 Spring…

TCP 協議設計入門:自定義消息格式與粘包解決方案

目錄 一、為什么需要自定義 TCP 協議&#xff1f; TCP粘包問題的本質 1.1 粘包與拆包的定義 1.2 粘包的根本原因 1.3 粘包的典型場景 二、自定義消息格式設計 2.1 協議結構設計 方案1&#xff1a;固定長度協議 方案2&#xff1a;分隔符標記法 方案3&#xff1a;長度前…

了解一下OceanBase中的表分區

OceanBase 是一個高性能的分布式關系型數據庫&#xff0c;它支持 SQL 標準的大部分功能&#xff0c;包括分區表。分區表可以幫助管理大量數據&#xff0c;提高查詢效率&#xff0c;通過將數據分散到不同的物理段中&#xff0c;可以減少查詢時的數據掃描量。 在 OceanBase 中操…

多線程網絡編程:粘包問題、多線程/多進程服務器實戰與常見問題解析

多線程網絡編程&#xff1a;粘包問題、多線程/多進程服務器實戰與常見問題解析 一、TCP粘包問題&#xff1a;成因、影響與解決方案 1. 粘包問題本質 TCP是面向流的協議&#xff0c;數據傳輸時沒有明確的消息邊界&#xff0c;導致多個消息可能被合并&#xff08;粘包&#xf…

大模型主干

1.什么是語言模型骨架LLM-Backbone,在多模態模型中的作用&#xff1f; 語言模型骨架&#xff08;LLM Backbone&#xff09;是多模態模型中的核心組件之一。它利用預訓練的語言模型&#xff08;如Flan-T5、ChatGLM、UL2等&#xff09;來處理各種模態的特征&#xff0c;進行語義…

[創業之路-350]:光刻機、激光器、自動駕駛、具身智能:跨學科技術體系全景解析(光-機-電-材-熱-信-控-軟-網-算-智)

光刻機、激光器、自動駕駛、具身智能四大領域的技術突破均依賴光、機、電、材、熱、信、控、軟、網、算、智十一大學科體系的深度耦合。以下從技術原理、跨學科融合、關鍵挑戰三個維度展開系統性分析&#xff1a; 一、光刻機&#xff1a;精密制造的極限挑戰 1. 核心技術與學科…

SVTAV1 編碼函數 svt_aom_is_pic_skipped

一 函數解釋 1.1 svt_aom_is_pic_skipped函數的作用是判斷當前圖片是否可以跳過編碼處理。 具體分析如下 函數邏輯 參數說明&#xff1a;函數接收一個指向圖片父控制集的指針PictureParentControlSet *pcs, 通過這個指針可以獲取與圖片相關的各種信息&#xff0c;用于判斷是否跳…

【Redis新手入門指南】從小白入門到日常使用(全)

文章目錄 前言redis是什么&#xff1f;定義原理與特點與MySQL對比 Redis安裝方式一、Homebrew 快速安裝 Redis&#xff08;推薦&#xff09;方式二、源碼編譯安裝redisHomebrew vs 源碼安裝對比 redis配置說明修改redis配置的方法常見redis配置項說明 redis常用命令redis服務啟…

Linux grep 命令詳解及示例大全

文章目錄 一、基本語法二、常用選項及示例1. 基本匹配&#xff1a;查找包含某字符串的行2. 忽略大小寫匹配 -i3. 顯示行號 -n4. 遞歸查找目錄下的文件 -r 或 -R5. 僅顯示匹配的字符串 -o6. 使用正則表達式 -E&#xff08;擴展&#xff09;或 egrep7. 顯示匹配前后行 -A, -B, -C…

【排序算法】快速排序(全坤式超詳解)———有這一篇就夠啦

【排序算法】——快速排序 目錄 一&#xff1a;快速排序——思想 二&#xff1a;快速排序——分析 三&#xff1a;快速排序——動態演示圖 四&#xff1a;快速排序——單趟排序 4.1&#xff1a;霍爾法 4.2&#xff1a;挖坑法 4.3&#xff1a;前后指針法 五&#xff1a;…

【platform push 提示 Invalid source ref: HEAD】

platform push 提示 Invalid source ref: HEAD 場景&#xff1a;環境&#xff1a;排查過程&#xff1a;解決&#xff1a; 場景&#xff1a; 使用platform push 命令行輸入git -v 可以輸出git 版本號&#xff0c;但就是提示Invalid source ref: HEAD&#xff0c;platform creat…

x-cmd install | Tuistash - Logstash 實時監控,告別圖形界面,高效便捷!

目錄 核心優勢&#xff0c;一覽無遺安裝適用場景&#xff0c;廣泛覆蓋功能亮點&#xff0c;不容錯過 還在為 Logstash 的監控而頭疼嗎&#xff1f;還在頻繁切換圖形界面查看數據嗎&#xff1f;現在&#xff0c;有了 Tuistash&#xff0c;一切都將變得簡單高效&#xff01; Tui…

【JEECG】BasicTable單元格編輯,插槽添加下拉組件樣式錯位

1.功能說明 BasicTable表格利用插槽&#xff0c;添加組件實現單元格編輯功能&#xff0c;選擇組件下拉框錯位 2.效果展示 3.解決方案 插槽內組件增加&#xff1a;:getPopupContainer"getPopupContainer" <template #salesOrderProductStatus"{ column, re…