如何快速掌握iOS三大主流架构设计模式

CtrlV艺术家
2026-06-13 06:52
阅读 2522

大家好,我是你们的学长,也是一名在高校带iOS实训课的讲师。作为一名211计算机专业的研究生,我在平时的学习和教学中发现,很多刚接触iOS开发的新人,代码写着写着就全堆在ViewController里,最后连自己都不敢改,一改就崩。

我当初学的时候也经历过这个“至暗时刻”,写出的代码被导师批得体无完肤。后来我痛定思痛,系统研究了iOS的架构设计,才算是真正入了门。写这篇教程,就是想用最通俗的语言,结合我这几年的实战和教学经验,帮零基础的新人彻底搞懂MVC、MVVM和VIPER这三种主流架构。

环境准备与辅助工具

在开始写代码前,我们需要准备好开发环境。

  1. 硬件与系统:一台Mac电脑,系统建议升级到最新的macOS。
  2. 开发工具:去App Store下载最新的Xcode。这是苹果官方的IDE,包含了编译器、模拟器和调试工具。
  3. 语言基础:本教程使用Swift语言,请确保你对Swift的基础语法(如类、结构体、协议、闭包)有初步了解。

学长的高效学习小贴士: 现在AI技术这么发达,大家一定要学会利用工具。我当初学的时候只能啃厚厚的英文文档,现在你们幸福多了。我强烈推荐大家使用 Anthropic 开发的Claude大模型来辅助学习,遇到不懂的架构概念直接问它,它能给你举出非常生动的例子。此外,B站和YouTube上有很多优质的 AI视频 教程,可以跟着视频里的UP主一起敲代码。甚至在实际工程中,我们有时候会用 Javascript 写一些自动化脚本,配合Sourcery等工具来批量生成VIPER架构中繁琐的模板代码,极大地提高了开发效率。

核心概念通俗解析

架构设计听起来很高大上,其实说白了就是“如何给代码分工”。我们来看看这三位“老熟人”。

1. MVC:经典的“大包大揽”

MVC全称是Model-View-Controller。

  • Model(模型):负责管理数据,比如从服务器获取用户信息。
  • View(视图):负责界面展示,比如按钮、文本框。
  • Controller(控制器):负责连接Model和View,处理用户的交互逻辑。

痛点:在iOS中,由于View和Controller耦合太深,导致Controller既要处理UI逻辑,又要处理网络请求和数据转换,最后变成了传说中的“Massive View Controller”(巨型视图控制器),几千行代码全塞在一个文件里。

2. MVVM:引入“翻译官”

MVVM全称是Model-View-ViewModel。它是MVC的进化版。

  • View和Model:职责和MVC一样。
  • ViewModel(视图模型):这是核心!它是一个“翻译官”,负责把Model里的数据转换成View能直接使用的格式,并且处理所有的业务逻辑。

优势:Controller变瘦了,它只负责把ViewModel的数据“绑定”到View上。ViewModel不依赖具体的UI,因此非常容易写单元测试。

3. VIPER:极致的“大公司分工”

VIPER不是那个杀毒软件,它是View、Interactor、Presenter、Entity、Routing的缩写。

  • View:纯展示,接收用户事件并告诉Presenter。
  • Interactor(交互器):包含核心业务逻辑和网络请求。
  • Presenter(展示器):接收View的事件,调用Interactor获取数据,然后格式化数据交给View展示。
  • Entity(实体):纯数据模型。
  • Routing(路由):负责页面跳转。

优势:极度的单一职责,每个文件都很小,非常适合大型团队协作。 劣势:代码量爆炸,写一个简单的页面可能要建五六个文件。

实战项目:用户资料展示

光说不练假把式。我们以一个“获取并展示用户个人资料”的功能为例,看看三种架构在代码层面到底有什么区别。

实战一:MVC模式下的用户资料

在MVC中,ViewController是个“全能王”。

import UIKit

// Model: 用户数据模型
struct User {
    let name: String
    let age: Int
}

// MVC中的ViewController
class UserMVCViewController: UIViewController {
    
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var ageLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 1. 发起网络请求 (业务逻辑)
        fetchUserData()
    }
    
    func fetchUserData() {
        // 模拟网络请求
        DispatchQueue.global().async {
            // 假设这里从服务器拿到了数据
            let user = User(name: "张三", age: 25)
            
            // 2. 更新UI (必须在主线程)
            DispatchQueue.main.async {
                self.nameLabel.text = "姓名:\(user.name)"
                self.ageLabel.text = "年龄:\(user.age)"
            }
        }
    }
}

学长点评:代码看起来很直观,但如果这个页面有几十个Label,还要处理各种复杂的表单验证,这个VC的代码量会迅速膨胀到无法维护。

实战二:MVVM模式下的用户资料

在MVVM中,我们把网络请求和数据格式化抽离到ViewModel中。为了对零基础友好,这里使用闭包回调来实现数据绑定(实际项目中推荐使用Combine框架)。

import UIKit

// Model
struct User {
    let name: String
    let age: Int
}

// ViewModel: 负责业务逻辑和数据转换
class UserViewModel {
    
    // 定义闭包,当数据更新时通知View
    var onDataUpdated: ((String, String) -> Void)?
    
    func fetchUser() {
        // 模拟网络请求
        DispatchQueue.global().async {
            let user = User(name: "李四", age: 28)
            
            // 在ViewModel中完成数据格式化
            let nameText = "姓名:\(user.name)"
            let ageText = "年龄:\(user.age)"
            
            DispatchQueue.main.async {
                // 通过闭包把格式化好的数据传给View
                self.onDataUpdated?(nameText, ageText)
            }
        }
    }
}

// View (ViewController变瘦了)
class UserMVVMViewController: UIViewController {
    
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var ageLabel: UILabel!
    
    // 持有ViewModel
    let viewModel = UserViewModel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 监听ViewModel的数据变化
        viewModel.onDataUpdated = { [weak self] name, age in
            self?.nameLabel.text = name
            self?.ageLabel.text = age
        }
        
        // 触发数据获取
        viewModel.fetchUser()
    }
}

学长点评:ViewController现在只负责“监听”和“展示”,具体的脏活累活都交给了ViewModel。这样即使UI大改,只要数据格式不变,ViewModel的代码一行都不用动。

实战三:VIPER模式下的用户资料

VIPER的代码量较大,我用文字流程图结合核心代码来展示。

VIPER交互流程

  1. View 触发加载事件 -> 通知 Presenter
  2. Presenter 调用 Interactor 获取数据
  3. Interactor 请求网络,拿到 Model(Entity) 返回给 Presenter
  4. Presenter 将 Model 转换为 View 需要的格式,通知 View 更新UI
// 1. 定义各模块之间的通信协议 (面向协议编程)
protocol UserVIPERViewProtocol: AnyObject {
    func showUserData(name: String, age: String)
}

protocol UserVIPERPresenterProtocol: AnyObject {
    func viewDidLoad()
}

protocol UserVIPERInteractorProtocol: AnyObject {
    func fetchUser(completion: @escaping (User) -> Void)
}

// 2. Entity (纯数据)
struct User {
    let name: String
    let age: Int
}

// 3. Interactor (干脏活的)
class UserInteractor: UserVIPERInteractorProtocol {
    func fetchUser(completion: @escaping (User) -> Void) {
        // 模拟网络请求
        DispatchQueue.global().async {
            let user = User(name: "王五", age: 30)
            DispatchQueue.main.async {
                completion(user)
            }
        }
    }
}

// 4. Presenter (承上启下的枢纽)
class UserPresenter: UserVIPERPresenterProtocol {
    weak var view: UserVIPERViewProtocol?
    var interactor: UserVIPERInteractorProtocol?
    
    func viewDidLoad() {
        interactor?.fetchUser { [weak self] user in
            // 数据格式化
            let nameText = "姓名:\(user.name)"
            let ageText = "年龄:\(user.age)"
            // 通知View更新
            self?.view?.showUserData(name: nameText, age: ageText)
        }
    }
}

// 5. View (纯粹的展示)
class UserVIPERViewController: UIViewController, UserVIPERViewProtocol {
    
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var ageLabel: UILabel!
    
    var presenter: UserVIPERPresenterProtocol?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        presenter?.viewDidLoad()
    }
    
    // 实现协议方法,更新UI
    func showUserData(name: String, age: String) {
        nameLabel.text = name
        ageLabel.text = age
    }
}

学长点评:看到了吧?VIPER把职责切分得极其细致。虽然看起来文件变多了,但每个类的代码都不超过50行。在几十人的大团队里,这种架构能避免多人修改同一个文件导致的代码冲突。

架构对比与选型指南

为了让大家更直观地理解,我整理了一个对比表格:

对比维度 MVC MVVM VIPER
代码复杂度
文件数量 少 (1-2个) 中 (2-3个) 多 (5个以上)
可测试性 差 (UI与逻辑耦合) 好 (ViewModel易测试) 极好 (完全解耦)
学习曲线 平缓 中等 陡峭
适用场景 小型项目、快速原型 中大型项目、主流选择 超大型项目、复杂业务
团队协作 易冲突 较好 极好

新手常见问题解答 (FAQ)

Q1:我是新手,第一个项目应该用哪种架构? 学长答:毫无疑问,先用MVC。不要一上来就搞VIPER,你会被繁琐的协议和文件绕晕的。先用MVC把功能跑通,当你发现ViewController代码超过500行,改一个Bug引出三个新Bug的时候,再尝试用MVVM去重构它。

Q2:MVVM一定要配合RxSwift或者Combine这种响应式框架吗? 学长答:不一定。对于零基础或者中小型项目,使用简单的闭包回调(如我上面实战代码所示)或者Delegate(代理)模式来实现数据绑定就足够了。响应式框架学习成本较高,建议把MVVM的思想掌握后,再去进阶学习Combine。

Q3:公司老项目全是MVC,老板让我重构,我该怎么下手? 学长答:千万不要试图一次性重构整个项目!采用“绞杀者模式”,在新开发的页面或者需要大改的页面使用MVVM,老页面保持MVC不动。慢慢让MVVM的页面占据主导,最终自然过渡。

学习建议与避坑指南

作为过来人,我给初学者的最后几点建议:

  1. 不要为了架构而架构:架构的目的是为了解决问题,而不是为了炫技。如果一个简单的设置页面,你用VIPER写了5个文件,那就是过度设计。
  2. 多读优秀开源代码:推荐去GitHub上看看像 CleanArchitectureRxSwift 这样的优秀项目,看看大神们是如何组织代码的。
  3. 画架构图:在写代码前,先用纸笔或者工具(如Draw.io)把模块之间的调用关系画出来。理清了数据流向,代码写起来会如丝般顺滑。
  4. 重视单元测试:MVVM和VIPER最大的优势之一就是好写测试。尝试为你的ViewModel写几个单元测试,这会极大地提升你的代码质量。

iOS开发是一场马拉松,架构设计是其中的重要补给站。希望这篇教程能帮你建立起正确的架构思维。如果在实践中遇到任何问题,欢迎在评论区留言,学长看到都会回复的。祝大家代码无Bug,早日成为iOS大佬!

评论 0

最热最新
暂无评论
CtrlV艺术家Lv.1
0
影响力
0
文章
0
粉丝