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

代码评审刺客
2025-12-18 18:12
阅读 1098

大家好,我是阿杰,一名在大厂干了3年iOS开发的工程师,平时也在B站做技术UP主,经常收到粉丝私信问我:“iOS架构到底该怎么学?”、“面试官总问MVC和MVVM的区别,我该怎么回答?”

我当初学的时候,也是一头雾水。看到“VIPER”、“Clean Architecture”这些词就发怵,以为是什么高深莫测的东西。其实,它们只是组织代码的方式不同而已,就像你整理书桌:有人按科目分,有人按颜色分,有人用抽屉分区——目的都是让东西更好找、更好用。

今天这篇教程,就是为完全零基础的同学准备的。我会用最通俗的语言,配合真实可运行的代码示例,带你一步步理解三种主流iOS架构:MVC、MVVM、VIPER。文章还会结合面试题场景,并简单聊聊它们和后端协作的关系。学完这篇,你不仅能写出更清晰的代码,还能在面试中自信作答!


一、为什么需要架构?不写架构不行吗?

先别急着敲代码!我们先搞清楚为什么要用架构

想象一下:你写了一个简单的天气App,只有一屏,显示温度。这时候,你把所有代码(网络请求、UI更新、数据处理)都塞进一个ViewController里,没问题,跑得飞快。

但随着功能增加——要支持城市切换、历史记录、夜间模式……你的ViewController可能膨胀到2000行!改一处,崩三处;想加个新功能,根本找不到入口。

架构的作用,就是帮你把代码“分门别类”,让每一部分只做自己该做的事。

💡 新手常见误区
“我项目小,不需要架构。”
错!哪怕只有1个页面,也建议用基础架构。养成好习惯,以后做大项目才不会手忙脚乱。


二、环境准备:你需要什么?

要跟着本教程动手实践,请确保:

  • 安装 Xcode 14 或更高版本(免费,Mac App Store下载)
  • 会创建一个 Single View App 项目
  • 基础Swift语法(变量、函数、类)已了解

⚠️ 注意:本文使用 Swift 编写,不涉及Objective-C。


三、核心概念:三种架构到底是什么?

我们用一个模拟登录功能作为例子贯穿全文:用户输入账号密码,点击登录,调用接口,成功则跳转主页。

1. MVC(Model-View-Controller)

这是苹果官方推荐的默认架构,也是你新建项目时的默认结构。

  • Model:数据模型,比如 User 结构体
  • View:UI界面,比如 UITextField, UIButton
  • ControllerViewController,负责协调 View 和 Model

✅ 优点:

  • 简单直观,适合小型项目
  • 苹果原生支持,文档多

❌ 缺点:

  • Controller 容易变成“大胖子”(Massive ViewController)
  • 逻辑耦合严重,难以测试

📝 代码示例(MVC)

// Model
struct User {
    let username: String
    let token: String
}

// Controller
class LoginViewController: UIViewController {
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    
    // 所有逻辑都在这里!
    @IBAction func loginTapped() {
        guard let username = usernameField.text,
              let password = passwordField.text else { return }
        
        // 模拟网络请求(实际应封装)
        NetworkService.shared.login(username: username, password: password) { [weak self] result in
            DispatchQueue.main.async {
                switch result {
                case .success(let user):
                    // 跳转主页
                    self?.navigateToHome(user: user)
                case .failure(let error):
                    self?.showError(error)
                }
            }
        }
    }
    
    private func navigateToHome(user: User) { /* ... */ }
    private func showError(_ error: Error) { /* ... */ }
}

🔍 面试题常考
“MVC的缺点是什么?”
标准答案:Controller承担了太多职责,导致代码难以维护和测试。


2. MVVM(Model-View-ViewModel)

为了解决MVC的“胖Controller”问题,MVVM把业务逻辑从Controller中抽出来,放到 ViewModel 中。

  • ViewViewController + UI组件(只负责展示和用户交互)
  • ViewModel:处理数据转换、网络请求、业务逻辑
  • Model:同MVC

✅ 优点:

  • Controller 变轻了
  • ViewModel 易于单元测试
  • 支持数据绑定(配合 Combine 或 RxSwift 更强)

❌ 缺点:

  • 学习成本略高
  • 小项目可能“杀鸡用牛刀”

📝 代码示例(MVVM)

// ViewModel
class LoginViewModel {
    var onLoginSuccess: ((User) -> Void)?
    var onLoginFailure: ((Error) -> Void)?
    
    func login(username: String, password: String) {
        NetworkService.shared.login(username: username, password: password) { [weak self] result in
            switch result {
            case .success(let user):
                self?.onLoginSuccess?(user)
            case .failure(let error):
                self?.onLoginFailure?(error)
            }
        }
    }
}

// Controller
class LoginViewController: UIViewController {
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    
    private let viewModel = LoginViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupBindings()
    }
    
    private func setupBindings() {
        viewModel.onLoginSuccess = { [weak self] user in
            self?.navigateToHome(user: user)
        }
        viewModel.onLoginFailure = { [weak self] error in
            self?.showError(error)
        }
    }
    
    @IBAction func loginTapped() {
        guard let username = usernameField.text,
              let password = passwordField.text else { return }
        viewModel.login(username: username, password: password)
    }
}

💡 亲身体验
我第一家公司用的就是MVVM,配合RxSwift做数据绑定,Controller真的清爽很多!


3. VIPER(View-Interactor-Presenter-Entity-Router)

这是最复杂但最解耦的架构,适合大型团队协作项目。

  • View:只负责UI展示,把用户操作转发给Presenter
  • Presenter:接收View事件,调用Interactor,格式化数据给View
  • Interactor:包含业务逻辑(相当于MVVM的ViewModel)
  • Entity:数据模型(相当于Model)
  • Router:负责页面跳转(解耦导航逻辑)

✅ 优点:

  • 高度解耦,模块职责清晰
  • 极易测试(每个组件都可独立Mock)
  • 团队协作时冲突少

❌ 缺点:

  • 文件数量爆炸(一个小功能5个文件)
  • 上手门槛高,小项目不划算

📝 代码示例(VIPER - 精简版)

// Entity
struct User { let username: String; let token: String }

// Interactor
protocol LoginInteractorInput {
    func login(username: String, password: String)
}

class LoginInteractor: LoginInteractorInput {
    weak var output: LoginInteractorOutput?
    
    func login(username: String, password: String) {
        NetworkService.shared.login(username: username, password: password) { result in
            switch result {
            case .success(let user):
                self.output?.didLoginSuccessfully(user: user)
            case .failure(let error):
                self.output?.didFailLogin(error: error)
            }
        }
    }
}

protocol LoginInteractorOutput: AnyObject {
    func didLoginSuccessfully(user: User)
    func didFailLogin(error: Error)
}

// Presenter
protocol LoginPresenterInput {
    func viewDidLoad()
    func loginButtonTapped(username: String, password: String)
}

class LoginPresenter: LoginPresenterInput {
    weak var view: LoginViewInput?
    var interactor: LoginInteractorInput
    var router: LoginRouterInput
    
    init(interactor: LoginInteractorInput, router: LoginRouterInput) {
        self.interactor = interactor
        self.router = router
    }
    
    func loginButtonTapped(username: String, password: String) {
        interactor.login(username: username, password: password)
    }
}

extension LoginPresenter: LoginInteractorOutput {
    func didLoginSuccessfully(user: User) {
        router.navigateToHome(user: user)
    }
    
    func didFailLogin(error: Error) {
        view?.showError(error)
    }
}

// View (ViewController)
protocol LoginViewInput: AnyObject {
    func showError(_ error: Error)
}

class LoginViewController: UIViewController, LoginViewInput {
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var passwordField: UITextField!
    
    var presenter: LoginPresenterInput!
    
    @IBAction func loginTapped() {
        guard let username = usernameField.text,
              let password = passwordField.text else { return }
        presenter.loginButtonTapped(username: username, password: password)
    }
    
    func showError(_ error: Error) { /* 显示错误 */ }
}

🤯 别慌!看起来文件多,但每个文件都很小,职责单一。
我当初第一次看VIPER代码,也觉得“这也太啰嗦了吧!”
但后来参与一个50人团队的金融App,才发现这种严格分工能避免无数bug。


四、实战对比:一张表说清楚区别

维度 MVC MVVM VIPER
文件数量 少(1个VC) 中(VC + VM) 多(5+文件)
Controller负担 极轻
可测试性 极好
学习成本
适合场景 小型Demo、快速原型 中大型App、注重测试 大型团队、长期维护项目
与后端协作 直接在VC调API 在VM封装API 在Interactor调用Service层

💬 关于“后端”的说明
无论哪种架构,网络请求都应该封装成独立的Service层(如NetworkService),而不是直接写在VC/VM/Interactor里。这样后端接口变更时,只需改Service,不影响上层逻辑。


五、新手常见问题解答

Q1:我该选哪种架构?

  • 刚入门 → 从 MVC 开始,理解基本分层
  • 想进阶/找工作 → 必学 MVVM
  • 面大厂/做复杂App → 了解 VIPER 思想(不一定要全用)

Q2:MVVM一定要用RxSwift或Combine吗?

不用!数据绑定是加分项,不是必需品。你可以像我上面那样,用闭包回调实现。

Q3:VIPER是不是过度设计?

对小项目是的。但如果你的App要维护3年以上,VIPER的前期投入是值得的。

Q4:面试被问“你怎么选架构”怎么答?

标准话术
“根据项目规模和团队情况选择。小项目用MVC快速迭代;中大型项目用MVVM保证可维护性和可测试性;超大型项目或金融类App考虑VIPER确保高度解耦。”


六、学习建议 & 避坑指南

✅ 正确学习路径:

  1. 先用MVC写2-3个小Demo(如TodoList、天气App)
  2. 把其中一个Demo改成MVVM,体会差异
  3. 阅读开源项目(如GitHub上的MVVM示例
  4. 尝试用VIPER写一个登录模块(不必全功能)

⚠️ 避坑提醒:

  • 不要为了架构而架构:先跑通功能,再考虑重构
  • 网络层必须独立:无论哪种架构,都要封装APIService
  • 别死记硬背:理解“为什么需要解耦”比记住定义更重要

📚 下一步学什么?

  • 深入学习 CombineRxSwift(用于MVVM数据绑定)
  • 了解 Coordinator 模式(解决VIPER的Router复杂度)
  • 研究 Clean Architecture(VIPER的升级版)

结语

架构不是银弹,没有“最好”,只有“最合适”。我当初学的时候,也是从一行行MVC代码开始,慢慢体会到解耦的价值。

希望这篇教程能帮你迈出第一步。如果你觉得有用,欢迎去B站搜“阿杰iOS”,我会持续更新更多零基础友好的实战教程!

最后送大家一句话:好的架构,是让你半年后回看代码时,还能看懂自己写的啥。

Happy Coding!🚀

评论 0

最热最新
暂无评论
代码评审刺客Lv.1
0
影响力
0
文章
0
粉丝