乐于分享
好东西不私藏

iOS 上的 MVC 与 MVVM:核心区别与实例对比

iOS 上的 MVC 与 MVVM:核心区别与实例对比
翻译:原文MVC Swift vs MVVM on iOS: Key Differences With Examples

MVC(模型-视图-控制器)和 MVVM(模型-视图-视图模型)是帮助 Swift 开发者构建各类实用移动应用的常用软件架构。了解这两种模式的核心区别,能帮你为下一个项目选择更合适的方案。

在现代 iOS 应用开发中,你需要根据项目复杂度、团队开发经验、交付周期等因素,为项目和终端用户选择合适的架构模式。在做出选择前,你需要理解它们的工作原理、解决的问题,以及可能带来的问题。本文将对比 iOS 环境中最常用的两种移动架构:苹果官方与第三方开发者广泛使用的 MVC 模式,以及源自微软生态的 MVVM 模式。


什么是 MVC 架构模式?

MVC 即模型-视图-控制器,是一种将应用拆分为三大核心逻辑组件的架构模式,每个组件负责处理应用的特定功能。

MVC 模式将 iOS 应用的职责拆分到不同模块,把应用的业务逻辑与操作层和展示层分离,并通过控制器这个中间层协调模型与视图的交互,包括从数据库获取数据、处理数据,再将数据回传数据库或用于界面渲染。

MVC 模式组件

MVC 将代码组织为三层结构:

  • 模型(Model):负责存储数据,为视图需求处理数据,完全不感知视图的存在。模型数据是构建精准展示界面的核心。
  • 视图(View):承担两项职责——向用户展示数据、接收用户操作。完全不感知模型,由其持有者(控制器)传入处理后的数据更新,因此视图不应包含任何业务逻辑。
  • 控制器(Controller):持有并通信视图与模型层,负责用模型的处理数据更新模型和视图。最简场景下,控制器通常作为视图的代理,或为视图设置回调,基于用户操作更新模型与视图。

MVC 核心组件关系

MVC 优点

  1. 简单易用:组件数量少、定义清晰,是最易上手和维护的架构之一。
  2. 职责分离清晰:视图和模型的归属边界明确。
  3. 可复用性高:视图和模型与其他组件耦合度低,易于复用。
  4. 学习资源丰富:iOS 早期最常用架构,开发者认知度高,学习资料充足。

MVC 缺点

  1. 控制器臃肿:不属于视图、模型的逻辑都会堆在控制器中,导致“庞大视图控制器(Massive View Controller)”问题。
  2. 可测试性差:业务逻辑集中在与视图、模型强耦合的控制器中,难以编写有效的单元测试。
  3. 维护与拓展困难:控制器越臃肿,单元测试覆盖越难,应用维护和迭代成本越高。

什么是 MVVM 架构模式?

MVVM 即模型-视图-视图模型,是一种强化 UI 与业务逻辑/后端逻辑分离的架构模式。它和 MVC 一样包含模型、视图组件,但用视图模型替代控制器充当中介,实现更清晰的职责分离。

为解决 MVC 的问题,MVVM 在 MVC 基础上新增视图模型(ViewModel)层(控制器仍保留),模型和视图的职责与 MVC 完全一致。

MVVM 模式组件

  • 模型(Model):同 MVC,存储数据,不感知视图,不为视图处理数据。
  • 视图(View):同 MVC,展示数据、接收用户操作,无业务逻辑,不感知模型。
  • 视图模型(ViewModel):模型与控制器之间的新增层,持有模型,将模型数据处理为视图可直接展示的格式;支持数据绑定,实现视图与模型的双向通信,自动同步数据变更。
  • 控制器(Controller):仅负责搭建和协调所持组件,业务逻辑已移至视图模型,相比 MVC 更轻量化;控制器直接持有视图模型,而非模型。

MVVM 核心组件关系

MVVM 优点

  1. 职责分离更清晰:视图、模型的边界更明确。
  2. 可复用性高:视图、模型耦合度低,复用更便捷。
  3. 普及度高:虽比 MVC 新,但近年在 iOS 开发中广泛使用,学习资源充足。
  4. 可测试性大幅提升:业务逻辑移至视图模型,可通过模拟模型数据直接测试视图模型的属性与方法。
  5. 可扩展性强、复杂度更低:应用规模、功能、用户流程增长时,内部结构复杂度远低于 MVC;可通过代理、绑定、回调灵活与视图模型通信,引入协调器(Coordinator)抽离导航逻辑后可升级为 MVVM-C 架构。

MVC 与 MVVM 实现示例

以待办应用中展示单条任务的组件为例,对比 MVC 和 MVVM 的实现差异。

两者通用代码

模型、视图、视图代理完全一致:

// 任务模型
structTask {
var text: String
var isChecked: Bool
}

// 视图代理
protocolTaskViewDelegate {
funcdidTap()
}

// 任务视图
finalclassTaskViewUIView {
weakvar delegate: TaskViewDelegate?

privatelet imageView: UIImageView
privatelet label: UILabel

// 省略初始化与点击事件绑定
funcupdate(withimageUIImage?, andattributedTextNSAttributedString) {
        imageView.image = image
        label.attributedText = attributedText
    }
}

MVC 控制器实现

控制器持有模型,负责将数据转换为视图可消费的格式:

finalclassTaskViewControllerUIViewController {
privatelet taskView: TaskView
privatelet attributes = [NSAttributedString.KeyAny]() // 演示用空属性
privatevar task: Task

// 省略视图搭建与初始更新
funcdidTap() {
        task.isChecked.toggle()
        updateView()
    }

funcupdateView() {
let image =UIImage(named: task.isChecked ?"checkedTaskIcon" : "taskIcon")
let attributedText =NSAttributedString(string: task.text, attributes: attributes)
        taskView.update(with: image, and: attributedText)
    }
}

MVVM 视图模型与控制器实现

视图模型

持有模型,处理数据并暴露视图所需属性:

structTaskViewModel {
var image: UIImage? {
UIImage(named: task.isChecked ?"checkedTaskIcon" : "taskIcon")
    }

var attributedText: NSAttributedString {
NSAttributedString(string: task.text, attributes: attributes)
    }

privatelet attributes = [NSAttributedString.KeyAny]()
privatevar task: Task

init(taskTask) {
self.task = task
    }

functoggleTask() {
        task.isChecked.toggle()
    }
}

视图模型单元测试示例

functestImage() {
// 给定测试数据
let task =Task(text: "Fixture Text", isChecked: true)
let sut =TaskViewModel(task: task)

// 验证结果
XCTAssertEqual(sut.image, UIImage(named: "checkedTaskIcon"))
}

MVVM 控制器实现

控制器仅协调视图与视图模型,逻辑大幅简化:

finalclassTaskViewControllerUIViewControllerTaskViewDelegate {
privatevar viewModel: TaskViewModel

// 省略视图搭建与初始更新
funcdidTap() {
        viewModel.toggleTask()
        updateView()
    }

funcupdateView() {
        taskView.update(with: viewModel.image, and: viewModel.attributedText)
    }
}

MVC 与 MVVM 实现总结

新增视图模型层后,控制器逻辑显著精简,原本堆在控制器的代码被封装到职责明确的结构体中。

MVVM 的测试优势尤为明显:可直接测试视图模型;而 MVC 需通过视图测试控制器逻辑(属于集成测试而非单元测试),或牺牲代码可读性暴露控制器属性,测试维护成本随复杂度急剧上升。


全文总结

架构选择是移动应用开发的核心决策,需结合项目实际需求判断:

  • 简单、无后续拓展计划的项目:MVC更合适,实现成本低、上手快。
  • 需长期维护、持续拓展、重视单元测试的项目:MVVM更优,可避免控制器臃肿,降低测试与迭代成本。

没有通用的完美架构,只有适配项目需求的最佳选择。