(二)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交互时。它将视图逻辑和业务逻辑分离,使得应用更加模块化,易于维护和扩展,但相对复杂,需要更多的学习成本。

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

相关推荐
StarkCoder1 天前
SwiftUI路由管理架构揭秘:从混乱到优雅的蜕变
前端框架
CocoaKier2 天前
苹果谷歌商店:如何监控并维护用户评分评论
ios·google·apple
青青家的小灰灰2 天前
React 架构进阶:自定义 Hooks 的高级设计模式与最佳实践
前端·react.js·前端框架
iOS日常2 天前
iOS设备崩溃日志获取与查看
ios·xcode
wangruofeng2 天前
AI 助力 Flutter 3.27 升级到 3.38 完整指南:两周踩坑与实战复盘
flutter·ios·ai编程
iOS日常3 天前
Xcode 垃圾清理
ios·xcode
开心就好20253 天前
不越狱能抓到 HTTPS 吗?在未越狱 iPhone 上抓取 HTTPS
后端·ios
一枚前端小姐姐3 天前
低代码平台表单设计系统技术分析(实战二)
低代码·架构·前端框架
傅里叶3 天前
iOS相机权限获取
flutter·ios
百思可瑞教育4 天前
Vue 前端与 Node.js 后端文件上传与处理实现
前端·javascript·vue.js·前端框架·node.js·ecmascript·百思可瑞教育