角色 职责 类比
Model 存储数据(计数值),不含业务逻辑 厨房里的食材(数字)
View 展示数据,捕捉用户点击事件 餐桌上的菜单和顾客(显示数字,点击按钮)
ViewModel 持有 Model,处理业务逻辑(加减),将数据格式化供 View 展示,并通过回调通知 View 更新 服务员(接收顾客指令,操作食材,把做好的菜端给顾客)
- Model(数据模型)
// CounterModel.h
@interface CounterModel : NSObject
@property (nonatomic, assign) NSInteger count;
@end
// CounterModel.m
@implementation CounterModel
@end
- ViewModel(业务逻辑 + 状态管理)
// CounterViewModel.h
@interface CounterViewModel : NSObject
// 初始化时传入 Model
- (instancetype)initWithModel:(CounterModel *)model;
// 业务方法
- (void)increment;
- (void)decrement;
// 格式化后的显示字符串(View 直接读取)
@property (nonatomic, strong, readonly) NSString *countString;
// 数据变化时通知 View 的回调(可换成 RAC 或 Combine,这里用 block)
@property (nonatomic, copy) void (^countDidChange)(NSInteger newCount);
@end
// CounterViewModel.m
@interface CounterViewModel ()
@property (nonatomic, strong) CounterModel *model;
@end
@implementation CounterViewModel
- (instancetype)initWithModel:(CounterModel *)model {
self = [super init];
if (self) {
_model = model;
}
return self;
}
- (void)increment {
self.model.count++;
[self notifyChange];
}
- (void)decrement {
self.model.count--;
[self notifyChange];
}
- (void)notifyChange {
if (self.countDidChange) {
self.countDidChange(self.model.count);
}
}
- (NSString *)countString {
return [NSString stringWithFormat:@"当前计数: %ld", (long)self.model.count];
}
@end
- View(ViewController / UIView)
// ViewController.m
@interface ViewController ()
@property (nonatomic, strong) CounterViewModel *viewModel;
@property (nonatomic, strong) UILabel *countLabel;
@property (nonatomic, strong) UIButton *plusButton;
@property (nonatomic, strong) UIButton *minusButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 创建 Model 和 ViewModel
CounterModel *model = [[CounterModel alloc] init];
model.count = 0;
self.viewModel = [[CounterViewModel alloc] initWithModel:model];
// 2. 绑定 ViewModel 的通知(当数据改变时更新 UI)
__weak typeof(self) weakSelf = self;
self.viewModel.countDidChange = ^(NSInteger newCount) {
// 更新 label 显示(从 ViewModel 读取格式化后的字符串)
weakSelf.countLabel.text = weakSelf.viewModel.countString;
};
// 3. 创建 UI 控件(代码简化,仅示意)
[self setupUI];
// 4. 初始显示
self.countLabel.text = self.viewModel.countString;
}
- (void)setupUI {
// 创建 label 和按钮,并添加约束(略)
// 假设已有 self.countLabel, self.plusButton, self.minusButton
[self.plusButton addTarget:self action:@selector(plusTapped) forControlEvents:UIControlEventTouchUpInside];
[self.minusButton addTarget:self action:@selector(minusTapped) forControlEvents:UIControlEventTouchUpInside];
}
- (void)plusTapped {
[self.viewModel increment]; // View 不关心业务逻辑,只调用 ViewModel
}
- (void)minusTapped {
[self.viewModel decrement];
}
@end
用户点击 “+” → plusTapped 调用 [self.viewModel increment]。
ViewModel 修改 Model 的 count,然后调用 notifyChange → 触发 countDidChange 回调。
ViewController 收到回调,更新 label.text = self.viewModel.countString(从 ViewModel 读取格式化后的字符串)。
屏幕上的数字更新。
为什么这是 MVVM
Model 只知道 count,没有任何 UI 相关代码。
ViewModel 持有 Model,提供业务方法(加减)和显示数据(countString),并且通过回调通知 View,绝不直接引用 View(无 UILabel、UIViewController 等)。
View(ViewController)只负责 UI 布局、事件响应和绑定回调,不包含任何加减逻辑或数据格式化。

image.png