(二)IOS开发-MVC和MVVM设计架构

前言

MVC(Model-View-Controller)和MVVM(Model-View-ViewModel)都是IOS开发中常用的设计架构,它们的核心目的是为了分离关注点,增强代码的可维护性、可扩展性和复用性。尽管它们都遵循分层的设计思想,但两者之间存在一些明显的区别,尤其是在如何组织业务逻辑和UI层之间的关系上。

具体内容

1. MVC(Model-View-Controller)

概述:在MVC架构中,应用分为三部分:Model(模型)、View(视图)和Controller(控制器)。

  • Model:代表数据层,负责应用的核心业务逻辑和数据操作。它独立于视图和控制器,只关心数据的存储和处理。
  • View:负责呈现数据给用户,并处理用户的输入(如点击、滑动等)。它只是UI界面的呈现层,尽量不包含业务逻辑。
  • Controller:控制器作为中介,处理视图和模型之间的交互,接收用户输入并根据输入更新模型,或者更新视图的显示内容。

特点

  • 视图和控制器的耦合较高:在传统的iOS MVC架构中,控制器往往既管理视图的布局和展示,又承担业务逻辑,容易造成"臃肿的控制器"(Massive View Controller)问题。
  • 适用于简单应用:MVC架构适合数据交互和界面展示较为简单的应用。

优点

  • 简单易理解,适用于简单的应用程序。
  • 可以较好地分离UI层和数据层的关注点。

缺点

  • 随着应用规模的增长,Controller容易变得复杂和臃肿。
  • 视图和控制器之间的耦合较高,导致难以扩展和维护。

2. MVVM(Model-View-ViewModel)

概述:MVVM是对MVC的改进,特别适用于数据绑定的应用。它引入了一个额外的层次------ViewModel,用来协调视图和模型之间的关系。MVVM模式特别适合于需要复杂数据交互和UI更新的应用。

  • Model:与MVC中的Model相同,表示应用的核心数据和业务逻辑。它与视图和ViewModel无关。
  • View:负责展示数据,通常是用户界面的部分。它只关心如何展示信息,不包含逻辑。它通常与ViewModel绑定,以便在数据变化时自动更新UI。
  • ViewModel:ViewModel是View与Model之间的桥梁,负责从Model获取数据,并将这些数据转换为View能够展示的格式。它不直接操作视图,但通常会使用数据绑定来通知View更新。

特点

  • ViewModel作为视图与模型的中介:ViewModel处理与视图的交互逻辑,把Model的数据转换成View需要的格式,并负责维护视图的状态。
  • 数据绑定 :MVVM的关键特点之一是通过数据绑定机制(如使用RxSwiftCombine等库)实现视图和ViewModel之间的同步,减少手动更新UI的代码量。
  • 适用于复杂UI:当应用中需要处理大量数据和复杂的UI交互时,MVVM模式表现得尤为强大。

优点

  • 解耦了视图和模型:视图和模型不直接交互,通过ViewModel来进行桥接。View只关心如何展示数据,Model只关心数据逻辑。
  • 提高了可测试性:ViewModel通常不依赖于具体的UI,因此更容易进行单元测试。
  • 便于数据绑定:通过数据绑定,视图和ViewModel之间的数据同步变得非常高效和自动化。

缺点

  • 相对复杂:对于小型或简单的应用,MVVM可能显得过于复杂,增加了额外的抽象层。
  • 学习成本:对于初学者来说,MVVM可能比MVC更难理解,尤其是涉及到数据绑定和Reactive编程时。

MVC与MVVM的对比

特性 MVC MVVM
架构层次 3层:Model、View、Controller 3层:Model、View、ViewModel
View与Model的关系 View通过Controller与Model交互 View通过ViewModel与Model交互
控制逻辑位置 控制器包含大部分业务逻辑和UI更新 逻辑被分配到ViewModel,Controller相对较轻
数据绑定 无内建的数据绑定机制,视图需要手动更新 数据绑定使得视图与ViewModel保持同步(如RxSwift)
适用场景 简单应用、UI更新较少、逻辑不复杂的应用 复杂UI和大量数据交互的应用
视图与控制器耦合度 较高,控制器处理视图的显示和数据更新 较低,视图和ViewModel之间的耦合度更小
可测试性 测试较困难,尤其是控制器的单元测试 更容易测试,ViewModel可以独立于UI进行单元测试
复杂度 适用于简单应用,复杂度低 适用于复杂应用,复杂度较高

示例:

我们要展示一个用户列表,其中每个用户有姓名和邮箱。应用会从网络上获取这些数据,并通过MVVM架构来管理数据和UI。

步骤 1:定义模型(Model)

模型表示应用的核心数据,我们定义一个User模型来表示每个用户。

rust 复制代码
// Model
struct User {
    let name: String
    let email: String
}

步骤 2:定义视图模型(ViewModel)

视图模型负责从模型中获取数据,并将数据转换为视图所需的格式。它还负责管理业务逻辑,例如处理加载数据的状态。

swift 复制代码
import Foundation
import RxSwift
import RxCocoa

// ViewModel
class UserListViewModel {

    private let disposeBag = DisposeBag()

    // Observable properties for the view to bind to
    var users: BehaviorRelay<[User]> = BehaviorRelay(value: [])
    var isLoading: BehaviorRelay<Bool> = BehaviorRelay(value: false)
    var errorMessage: BehaviorRelay<String?> = BehaviorRelay(value: nil)

    // Fetch data (模拟网络请求)
    func fetchUsers() {
        isLoading.accept(true)
        // 模拟网络请求,1秒后返回数据
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            let sampleUsers = [
                User(name: "John Doe", email: "john.doe@example.com"),
                User(name: "Jane Smith", email: "jane.smith@example.com")
            ]

            // 在主线程更新数据
            DispatchQueue.main.async {
                self.isLoading.accept(false)
                self.users.accept(sampleUsers)
            }
        }
    }
}

步骤 3:定义视图(View)

视图负责展示数据并响应用户交互。在这里,我们使用一个UIViewController来表示视图,它会从视图模型中绑定数据并展示给用户。

swift 复制代码
import UIKit
import RxSwift
import RxCocoa

// View
class UserListViewController: UIViewController {
    
    private let disposeBag = DisposeBag()
    private let viewModel = UserListViewModel()
    
    // UI Elements
    private let tableView = UITableView()
    private let loadingIndicator = UIActivityIndicatorView(style: .large)
    private let errorLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupUI()
        bindViewModel()
        
        // Fetch users data
        viewModel.fetchUsers()
    }
    
    private func setupUI() {
        view.addSubview(tableView)
        view.addSubview(loadingIndicator)
        view.addSubview(errorLabel)
        
        // Layout your UI components here (simplified for example)
        tableView.frame = view.bounds
        loadingIndicator.center = view.center
        errorLabel.frame = CGRect(x: 20, y: 100, width: view.frame.width - 40, height: 50)
        
        errorLabel.textColor = .red
        errorLabel.isHidden = true
        
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UserCell")
    }
    
    private func bindViewModel() {
        // Binding the users data from ViewModel to the tableView
        viewModel.users
            .bind(to: tableView.rx.items(cellIdentifier: "UserCell")) { (index, user, cell) in
                cell.textLabel?.text = "(user.name) - (user.email)"
            }
            .disposed(by: disposeBag)
        
        // Binding the loading state to the loading indicator
        viewModel.isLoading
            .subscribe(onNext: { [weak self] isLoading in
                self?.loadingIndicator.isHidden = !isLoading
                if isLoading {
                    self?.loadingIndicator.startAnimating()
                } else {
                    self?.loadingIndicator.stopAnimating()
                }
            })
            .disposed(by: disposeBag)
        
        // Binding the error message
        viewModel.errorMessage
            .subscribe(onNext: { [weak self] errorMessage in
                self?.errorLabel.text = errorMessage
                self?.errorLabel.isHidden = errorMessage == nil
            })
            .disposed(by: disposeBag)
    }
}

步骤 4:运行应用

在应用启动时,UserListViewController会请求数据并通过UserListViewModel来获取数据。视图模型通过BehaviorRelay将数据传递给视图,并通过RxSwift将数据绑定到UI组件(如UITableView)。

代码解释

  • ModelUser结构体表示用户数据。
  • ViewModelUserListViewModel管理应用的逻辑。它负责获取用户数据(模拟网络请求),并通过BehaviorRelay通知视图更新数据。BehaviorRelay是RxSwift的一种类型,它能提供实时的可观察数据流。
  • ViewUserListViewController展示用户列表,并通过RxSwift的数据绑定来实时更新UI。它会绑定ViewModel中的数据,并在数据发生变化时自动更新UI。

我们在这个例子中使用了RxSwift来实现数据绑定和响应式编程。视图模型通过BehaviorRelay来提供数据流,视图通过订阅这些数据流来自动更新UI。RxSwift使得视图和视图模型之间的交互变得更加简单和高效,避免了直接的回调和手动更新UI的操作。

总结

  • MVC:适合小型应用或简单的界面,易于理解和实现,但容易导致控制器过于臃肿,且难以扩展。适合数据交互较简单的应用。
  • MVVM:适合复杂的应用,尤其是在需要大量数据绑定和UI交互时。它将视图逻辑和业务逻辑分离,使得应用更加模块化,易于维护和扩展,但相对复杂,需要更多的学习成本。

两种模式各有优劣,选择哪种架构取决于应用的复杂度、开发团队的经验以及开发流程的需求。

相关推荐
HarderCoder9 小时前
iOS 知识积累第一弹:从 struct 到 APP 生命周期的全景复盘
ios
薛定谔的算法10 小时前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
小刘大王19 小时前
while循环与死循环
架构·前端框架
叽哥19 小时前
Flutter Riverpod上手指南
android·flutter·ios
断竿散人20 小时前
乾坤微前端框架的沙箱技术实现原理深度解析
前端·javascript·前端框架
用户092 天前
SwiftUI Charts 函数绘图完全指南
ios·swiftui·swift
YungFan2 天前
iOS26适配指南之UIColor
ios·swift
权咚2 天前
阿权的开发经验小集
git·ios·xcode
用户092 天前
TipKit与CloudKit同步完全指南
ios·swift
ygria3 天前
样式工程化:如何实现Design System
前端·前端框架·前端工程化