在 iOS Swift 开发中,Context 和 ViewModel 是两个不同层级的概念,它们分别服务于不同的架构模式和代码组织需求。以下是两者的核心区别与联系:
一、核心职责对比
- Context(上下文)
• 角色定位:
Context 是协调不同组件间交互的“中介者”,通常用于管理多个模块(如 Controller、Presenter、ViewModel)之间的通信和生命周期。它的核心目标是解耦组件间的直接依赖,通过统一的协议或路由机制传递消息。
• 功能特性:
• 生命周期管理:强持有相关组件(如 Presenter、ViewModel),确保其随 Controller 释放而销毁,避免内存泄漏。
• 跨组件交互:作为桥梁,允许 Presenter 与 ViewModel、甚至多个 Presenter 之间通过协议或 URL 路由间接通信。
• 状态共享:在复杂视图层级中,Context 可通过父视图链传递(如 UIView 的 superview.context
),使得子视图无需直接引用 Controller 即可共享业务逻辑。
- ViewModel
ViewModel 是 MVVM(Model-View-ViewModel)模式的核心组件:
• 角色定位:
ViewModel 是数据驱动层,负责将原始 Model 数据转换为 View 可直接使用的格式,并封装与业务逻辑相关的操作(如网络请求、表单校验)。
• 功能特性:
• 数据转换:将 Model 的原始数据(如 API 响应)映射为 View 需要的结构化数据(如格式化字符串、日期)。
• 状态管理:通过响应式编程(如 Combine 的 @Published
)或数据绑定(如 SwiftUI 的 @ObservedObject
)通知 View 更新。
• 业务逻辑封装:处理用户交互事件(如按钮点击),并触发 Model 层的更新或服务调用。
二、应用场景差异
- Context 的典型使用场景
• 中间路由架构:在 MVC、MVP 或 MVVM 混合架构中,Context 作为全局路由协调多个 Presenter 或 ViewModel 的交互(例如通过 URL 协议跳转不同模块)。
• 组件化开发:在大型项目中,通过 Context 实现模块间解耦(如 A 模块的 Presenter 通过 Context 调用 B 模块的 ViewModel 方法)。
• 状态传递:在复杂视图层级中,子视图通过父视图的 Context 获取共享的业务状态,避免直接依赖上层 Controller。
- ViewModel 的典型使用场景
• 数据绑定:在 SwiftUI 或 UIKit 中,将 ViewModel 的@Published
属性绑定到 UI 控件(如TextField
的文本输入),实现自动同步。
• 单元测试:独立于 View 测试业务逻辑(例如验证表单校验规则或网络请求处理)。
• 响应式编程:通过 RxSwift 或 Combine 实现异步数据流处理(如实时搜索框的防抖逻辑)。
三、实现方式与数据流向
- Context 的实现
• 强引用与弱引用:Controller 强持有 Context,Context 强持有 Presenter/ViewModel,但 Presenter/ViewModel 弱引用 Context 以避免循环引用。
• 协议交互:通过定义 @protocol
声明交互接口,例如 LSRouteHeadViewDelegate
协议用于通知头部视图更新。
• 动态关联:通过 Objective-C Runtime 的 objc_setAssociatedObject
将 Context 附加到任意 NSObject 子类(如 UIView),实现“万物皆可携带上下文”。
- ViewModel 的实现
• 响应式属性:使用ObservableObject
协议和@Published
属性包装器(Combine 框架)驱动 UI 更新。
• 命令模式:定义 Command
结构体封装用户操作(如提交表单),并通过 ViewModel 的方法触发。
• 依赖注入:通过构造函数或属性注入外部服务(如网络层 APIService
),提升可测试性。
四、总结:核心区别
维度 | Context | ViewModel |
---|---|---|
核心职责 | 协调组件间通信与生命周期管理 | 数据转换与业务逻辑封装 |
所属架构 | 中间路由、CDD(Context-Driven Design) | MVVM、Clean Architecture |
数据流向 | 消息转发、状态共享 | 单向/双向数据绑定 |
典型实现 | 协议、动态关联对象 | 响应式属性(@Published )、命令模式 |
适用场景 | 复杂模块交互、组件化解耦 | 数据驱动 UI、单元测试友好 |
五、实际代码示例
Context 的典型使用(Objective-C 伪代码)
// Context 定义
class RouterContext {
weak var controller: RouterController?
var presenter: RouterPresenter?
var viewModel: RouterViewModel?
}
// Controller 中初始化
controller.context = RouterContext()
controller.context.presenter = RouterPresenter()
controller.context.viewModel = RouterViewModel()
// 子视图通过父视图链获取 Context
extension UIView {
var context: RouterContext? {
return superview?.context // 递归查找父视图的 Context
}
}
ViewModel 的典型实现(SwiftUI)
// ViewModel 定义
class LoginViewModel: ObservableObject {
@Published var username: String = ""
@Published var password: String = ""
func login() {
APIService.shared.login(username: username, password: password) { result in
// 处理登录结果并更新 UI
}
}
}
// View 中绑定
struct LoginView: View {
@ObservedObject var viewModel = LoginViewModel()
var body: some View {
TextField("用户名", text: $viewModel.username)
SecureField("密码", text: $viewModel.password)
Button("登录") { viewModel.login() }
}
}
六、扩展建议
• Context 的进阶应用:在 VIPER 或 Clean Architecture 中,Context 可与 Router 结合实现跨模块导航。
• ViewModel 的优化:通过 Combine 的 CurrentValueSubject
或 PassthroughSubject
替代部分 @Published
属性,实现更灵活的数据流控制。