iOS应用架构设计:MVC、MVVM、VIPER对比(零基础入门教程)
大家好,我是你们的iOS讲师,也是一名开源项目维护者。我写过不少技术文档,也带过很多刚入行的同学。今天写这篇教程,是因为我当初学 iOS 的时候,最头疼的就是“架构”这个词——听起来高大上,但没人告诉我它到底对写代码有什么实际帮助。
更关键的是,如果你正在准备求职,简历上如果能清晰写出你用过哪种架构、为什么选它、解决了什么问题,会非常加分!所以今天,我们就从零开始,手把手搞懂 iOS 里最常用的三种架构:MVC、MVVM 和 VIPER。
💡 本文适合完全零基础的新手。不需要你懂 Swift 高级语法,也不需要你会 Xcode 所有功能,只要会写
print("Hello")就够了!
一、什么是“应用架构”?为什么要学?
简单说,架构就是你组织代码的方式。
想象你要搭一个乐高城堡:
- 如果随便堆零件,很快就会乱成一团,改一个小窗户可能整个塔楼塌掉;
- 但如果你先规划好“地基”、“城墙”、“塔楼”的模块,每个部分各司其职,修改起来就轻松多了。
在 iOS 开发中,架构决定了你的代码怎么分工、谁负责显示界面、谁负责处理数据、谁负责业务逻辑。
好的架构能让你:
- 代码更容易看懂
- 出 bug 时更快定位
- 团队协作不打架
- 面试时自信地说出“我用 MVVM 解耦了 UI 和逻辑”
二、环境准备(5分钟搞定)
我们用 Apple 官方工具开发,免费且简单:
- 安装 Xcode
- 打开 Mac App Store
- 搜索 “Xcode”,点击安装(约8GB,喝杯咖啡等一下)
- 创建新项目
- 打开 Xcode → “Create a new Xcode project”
- 选择 “App” → Next
- Product Name 填
ArchitectureDemo - Interface 选 Storyboard
- Language 选 Swift
- 其他默认 → Next → 保存到桌面
✅ 现在你就有了一个空白的 iOS 项目!运行一下(点击左上角 ▶️),应该能看到白屏。
三、核心概念:三大架构通俗解释
我们用一个天气 App的例子来理解(界面上显示城市名和温度)。
1. MVC(Model-View-Controller)——苹果官方推荐
这是 iOS 最基础、最经典的架构,也是 Xcode 默认项目采用的方式。
- Model:数据本身(比如
Weather(city: "北京", temp: 25)) - View:界面上你能看到的东西(UILabel、UIButton)
- Controller:中间人,协调 Model 和 View(通常是
ViewController)
🧠 我当初的理解误区:以为 ViewController 只是“控制界面”,其实它要干所有活——网络请求、数据解析、UI 更新全塞在里面!
示例代码(MVC)
// Model
struct Weather {
let city: String
let temperature: Int
}
// ViewController(Controller + 部分逻辑)
class WeatherViewController: UIViewController {
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var tempLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// 模拟获取数据(现实中可能是网络请求)
let weather = Weather(city: "杭州", temperature: 30)
// 直接更新 UI
cityLabel.text = weather.city
tempLabel.text = "\(weather.temperature)°C"
}
}
✅ 优点:简单直接,适合小项目
❌ 缺点:ViewController 很快变得又胖又难维护(业界戏称 “Massive View Controller”)
2. MVVM(Model-View-ViewModel)——目前最流行
为了解决 MVC 的“胖控制器”问题,MVVM 把逻辑抽离到 ViewModel 中。
- Model:还是数据(不变)
- View:还是界面(不变)
- ViewModel:专门处理数据转换和业务逻辑,不碰 UI 组件
关键思想:View 只负责显示,ViewModel 只管提供“显示用的数据”。
示例代码(MVVM)
// Model(同上)
struct Weather {
let city: String
let temperature: Int
}
// ViewModel
class WeatherViewModel {
let weather: Weather
init(weather: Weather) {
self.weather = weather
}
// 提供给 View 的格式化数据
var displayCity: String {
return "📍 \(weather.city)"
}
var displayTemperature: String {
return "\(weather.temperature)°C"
}
}
// ViewController(只负责绑定)
class WeatherViewController: UIViewController {
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var tempLabel: UILabel!
private var viewModel: WeatherViewModel!
override func viewDidLoad() {
super.viewDidLoad()
let weather = Weather(city: "成都", temperature: 28)
viewModel = WeatherViewModel(weather: weather)
// 绑定数据(无逻辑)
cityLabel.text = viewModel.displayCity
tempLabel.text = viewModel.displayTemperature
}
}
✅ 优点:
- ViewController 变瘦了
- ViewModel 容易单元测试
- 逻辑复用性强
❌ 缺点:初学者容易搞不清“哪些该放 ViewModel”
3. VIPER(View-Interactor-Presenter-Entity-Router)——企业级解耦
当你项目超大、团队超多时,MVVM 可能还不够。VIPER 是彻底的分层架构,每个角色职责单一。
五个角色:
- View:只管 UI 事件(按钮点击)和显示
- Presenter:接收 View 请求,调用 Interactor,把结果转给 View
- Interactor:核心业务逻辑(网络、数据库)
- Entity:纯数据模型(类似 Model)
- Router:页面跳转导航
🧠 我第一次看 VIPER 时觉得太复杂,但后来做金融类 App 时,发现它让多人协作效率翻倍!
简化版代码结构(VIPER)
// Entity
struct WeatherEntity {
let city: String
let temperature: Int
}
// Interactor(业务逻辑)
class WeatherInteractor {
func fetchWeather(completion: (WeatherEntity) -> Void) {
// 模拟网络请求
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let weather = WeatherEntity(city: "西安", temperature: 32)
completion(weather)
}
}
}
// Presenter
class WeatherPresenter {
weak var view: WeatherViewProtocol?
private let interactor = WeatherInteractor()
func viewDidLoad() {
interactor.fetchWeather { [weak self] entity in
let displayCity = "📍 \(entity.city)"
let displayTemp = "\(entity.temperature)°C"
self?.view?.showWeather(city: displayCity, temp: displayTemp)
}
}
}
// View Protocol(解耦的关键!)
protocol WeatherViewProtocol: AnyObject {
func showWeather(city: String, temp: String)
}
// ViewController 实现协议
class WeatherViewController: UIViewController, WeatherViewProtocol {
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var tempLabel: UILabel!
private let presenter = WeatherPresenter()
override func viewDidLoad() {
super.viewDidLoad()
presenter.view = self
presenter.viewDidLoad()
}
func showWeather(city: String, temp: String) {
cityLabel.text = city
tempLabel.text = temp
}
}
✅ 优点:极致解耦,每个文件职责清晰,适合大型项目
❌ 缺点:文件多、样板代码多,小项目显得“杀鸡用牛刀”
四、实战:用三种架构各做一个“计数器 App”
我们来做个超简单的 App:界面上一个数字,点“+”按钮加1。
步骤 1:在 Storyboard 中添加 UI
- 拖一个
UILabel到屏幕中央,文字设为0,Tag 设为100 - 拖一个
UIButton,文字设为+,Tag 设为200
步骤 2:分别实现三种架构
📌 注意:以下代码都放在
ViewController.swift中,你可以注释掉不用的部分来切换
✅ MVC 版本(最简单)
class ViewController: UIViewController {
var count = 0
override func viewDidLoad() {
super.viewDidLoad()
if let label = view.viewWithTag(100) as? UILabel {
label.text = "\(count)"
}
if let button = view.viewWithTag(200) as? UIButton {
button.addTarget(self, action: #selector(increment), for: .touchUpInside)
}
}
@objc func increment() {
count += 1
if let label = view.viewWithTag(100) as? UILabel {
label.text = "\(count)"
}
}
}
✅ MVVM 版本(逻辑分离)
// ViewModel
class CounterViewModel {
var count = 0
func increment() {
count += 1
}
var displayCount: String {
return "\(count)"
}
}
// ViewController
class ViewController: UIViewController {
private let viewModel = CounterViewModel()
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
if let button = view.viewWithTag(200) as? UIButton {
button.addTarget(self, action: #selector(increment), for: .touchUpInside)
}
}
@objc func increment() {
viewModel.increment()
updateUI()
}
private func updateUI() {
if let label = view.viewWithTag(100) as? UILabel {
label.text = viewModel.displayCount
}
}
}
✅ VIPER 版本(完整分层)
由于篇幅,这里展示核心思路。实际项目中每个角色应放在不同文件。
// Entity
struct CounterEntity {
var value: Int
}
// Interactor
class CounterInteractor {
private var counter = CounterEntity(value: 0)
func increment() -> Int {
counter.value += 1
return counter.value
}
}
// Presenter
class CounterPresenter {
weak var view: CounterViewProtocol?
private let interactor = CounterInteractor()
func didTapIncrement() {
let newValue = interactor.increment()
view?.displayCount("\(newValue)")
}
func viewDidLoad() {
view?.displayCount("0")
}
}
// Protocol
protocol CounterViewProtocol: AnyObject {
func displayCount(_ text: String)
}
// ViewController
class ViewController: UIViewController, CounterViewProtocol {
private let presenter = CounterPresenter()
override func viewDidLoad() {
super.viewDidLoad()
presenter.view = self
presenter.viewDidLoad()
if let button = view.viewWithTag(200) as? UIButton {
button.addTarget(self, action: #selector(tapIncrement), for: .touchUpInside)
}
}
@objc func tapIncrement() {
presenter.didTapIncrement()
}
func displayCount(_ text: String) {
if let label = view.viewWithTag(100) as? UILabel {
label.text = text
}
}
}
五、三大架构对比表
| 特性 | MVC | MVVM | VIPER |
|---|---|---|---|
| 学习难度 | ⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
| 代码量 | 少 | 中 | 多 |
| ViewController 职责 | 很重 | 轻 | 极轻 |
| 测试友好度 | 差 | 好 | 极好 |
| 适合项目规模 | 小型 Demo | 中小型 App | 大型/团队项目 |
| 简历关键词 | “熟悉 MVC” | “使用 MVVM 解耦” | “采用 VIPER 架构提升可维护性” |
六、新手常见问题解答(FAQ)
Q1:我该从哪个架构开始学?
A:先掌握 MVC(因为它是基础),然后立刻上手 MVVM。90% 的公司项目用 MVVM 足够了。VIPER 等工作后再深入。
Q2:MVVM 一定要用 RxSwift 或 Combine 吗?
A:不需要! 很多人被误导了。上面的 MVVM 示例就是“手动绑定”,完全没用响应式框架。只有当 UI 需要自动响应数据变化时(比如搜索框实时过滤),才考虑用 RxSwift。
Q3:我的 ViewController 还是很胖,怎么办?
A:检查是否把网络请求、数据解析、本地存储全塞进去了。尝试把这些逻辑移到 Service 类或 ViewModel 中。
Q4:简历上怎么写架构经验?
A:不要只写“使用 MVVM”,要写:
“在 XX 项目中采用 MVVM 架构,将业务逻辑从 ViewController 中剥离至 ViewModel,使单元测试覆盖率提升至 70%,并支持多端 UI 复用。”
七、学习建议 & 下一步路线
- 动手实践:把今天的计数器 App 用三种方式都写一遍,感受差异。
- 阅读源码:GitHub 上搜 “iOS MVVM example”,看别人怎么组织代码。
- 进阶方向:
- 学习 Coordinator 模式(解决导航逻辑混乱)
- 了解 Clean Architecture(更通用的架构思想)
- 尝试 SwiftUI + MVVM(苹果新框架的最佳拍档)
- 避坑指南:
- 不要为了用 VIPER 而用 VIPER
- ViewModel 不要持有 View 的引用(会造成循环引用)
- 架构是手段,不是目的——目标是写出可维护、可测试、可协作的代码
结语
架构不是银弹,但它是你从“写得出功能”迈向“写得好代码”的关键一步。我当初也是从一行行混乱的 ViewController 走过来的,现在回头看,正是这些架构思想让我在技术分享、开源项目和简历上都有了底气。
希望这篇教程能帮你少走弯路。如果你照着做了,欢迎在评论区留言你的收获!
📌 技术分享的价值,在于让更多人少踩坑。如果你觉得有用,请转发给正在学 iOS 的朋友!

评论 0