iOS应用架构设计:MVC、MVVM、VIPER对比(零基础入门教程)

线上稳定吗
2025-12-16 02:49
阅读 688

大家好,我是你们的iOS讲师,也是一名开源项目维护者。我写过不少技术文档,也带过很多刚入行的同学。今天写这篇教程,是因为我当初学 iOS 的时候,最头疼的就是“架构”这个词——听起来高大上,但没人告诉我它到底对写代码有什么实际帮助。

更关键的是,如果你正在准备求职,简历上如果能清晰写出你用过哪种架构、为什么选它、解决了什么问题,会非常加分!所以今天,我们就从零开始,手把手搞懂 iOS 里最常用的三种架构:MVC、MVVM 和 VIPER

💡 本文适合完全零基础的新手。不需要你懂 Swift 高级语法,也不需要你会 Xcode 所有功能,只要会写 print("Hello") 就够了!


一、什么是“应用架构”?为什么要学?

简单说,架构就是你组织代码的方式

想象你要搭一个乐高城堡:

  • 如果随便堆零件,很快就会乱成一团,改一个小窗户可能整个塔楼塌掉;
  • 但如果你先规划好“地基”、“城墙”、“塔楼”的模块,每个部分各司其职,修改起来就轻松多了。

在 iOS 开发中,架构决定了你的代码怎么分工、谁负责显示界面、谁负责处理数据、谁负责业务逻辑

好的架构能让你:

  • 代码更容易看懂
  • 出 bug 时更快定位
  • 团队协作不打架
  • 面试时自信地说出“我用 MVVM 解耦了 UI 和逻辑”

二、环境准备(5分钟搞定)

我们用 Apple 官方工具开发,免费且简单:

  1. 安装 Xcode
    • 打开 Mac App Store
    • 搜索 “Xcode”,点击安装(约8GB,喝杯咖啡等一下)
  2. 创建新项目
    • 打开 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 复用。”


七、学习建议 & 下一步路线

  1. 动手实践:把今天的计数器 App 用三种方式都写一遍,感受差异。
  2. 阅读源码:GitHub 上搜 “iOS MVVM example”,看别人怎么组织代码。
  3. 进阶方向
    • 学习 Coordinator 模式(解决导航逻辑混乱)
    • 了解 Clean Architecture(更通用的架构思想)
    • 尝试 SwiftUI + MVVM(苹果新框架的最佳拍档)
  4. 避坑指南
    • 不要为了用 VIPER 而用 VIPER
    • ViewModel 不要持有 View 的引用(会造成循环引用)
    • 架构是手段,不是目的——目标是写出可维护、可测试、可协作的代码

结语

架构不是银弹,但它是你从“写得出功能”迈向“写得好代码”的关键一步。我当初也是从一行行混乱的 ViewController 走过来的,现在回头看,正是这些架构思想让我在技术分享、开源项目和简历上都有了底气。

希望这篇教程能帮你少走弯路。如果你照着做了,欢迎在评论区留言你的收获!

📌 技术分享的价值,在于让更多人少踩坑。如果你觉得有用,请转发给正在学 iOS 的朋友!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝