最近公司招了一个高级开发,采用的流行的MVVM+RAC+cocopods私有库开发。一开始我一直使用的事MVC的经典模式,也没接触过组件化,在这里先简单说说采用MVVM模式开发的想法。
MVC:Model View Controller (Massive View controller重量级视图控制器)
从上面可以看到原来MVC模式下Controller的占比,想要未视图控制器瘦身,并且进一步分离事物,MVVM华丽登场。
MVVM:Model View View-Model
1.Model 模型层,或许会也或许不会封装一些额外的业务逻辑工作,我更偏向于单纯的数据模型对象。
2.View 视图本身当然也包含视图控制器本身,和用户互动的都在视图层面。
3.ViewModel
在实际的项目中,会将网络请求模块、数据解析、以及一些业务逻辑处理等都放在这个继承于NSObject类的ViewModel文件中,最终只暴露给View或者Controller一个需要的结果。
一个更好的术语可能是 “View Coordinator”(感谢[Dave Lee](https://twitter.com/kastiglione)
提的这个 “View Coordinator” 术语, 真是个好点子). 你可以认为它就像是电视新闻主播背后的研究人员和作家团队. 它从必要的资源(数据库, 网络服务调用, 等)中获取原始数据, 运用逻辑, 并处理成 view (controller) 的展示数据. 它(通常通过属性)暴露给视图控制器需要知道的仅关于显示视图工作的信息(理想地你不会暴漏你的 data-model 对象). 它还负责对上游数据的修改(比如更新模型/数据库, API POST 调用).
对于Model View ViewModel Controller 的引用关系,Contrller、View引用ViewModel, ViewModel引用Model、ViewModel,并且是单向引用(这里的单向引用,我还不是特别明白是不是必须,但至少在结合cocopods私有库开发的时候,因为依赖关系必须单向,所以必须是单向引用,也许也就是因为私有库而限制的这一点)。
一个视图可以对应一个ViewModel,也可以多个视图对应一个ViewModel,也可以一个视图对应多个ViewModel。为了方便代码的阅读性,推荐一个视图对应一个ViewModel以及ViewModel中引用子ViewModel,这样处理起来非常的清晰有条理。
粘贴部分代码看下基本的MVVM 实现列表的基本引用过程:
VC:
#import "TestViewModel.h"
#import "TestCellViewModel.h"
#import "TestModel.h"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TestCellViewModel *cellViewModel = self.viewModel.list[indexPath.section];
TestCell *cell = [tableView dequeueReusableCellWithIdentifier:grayCellIdentifier];
if (!cell) {
cell = [[TestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:grayCellIdentifier];
}
[cell renderWithViewModel:cellViewModel];
return cell;
}
- (TestViewModel *)viewModel {
if (!_viewModel) {
_viewModel = [[TestViewModel alloc] init];
}
return _viewModel;
}
ViewModel:
.h
@interface TestViewModel : NSObject
@property (nonatomic, copy, readonly) NSArray *list;
@end
.m:
@interface TestViewModel()
@property (nonatomic, strong) NSMutableArray *mulDataSourceArr;
@end
@implementation TestViewModel
- (instancetype)init {
if (self = [super init]) {
[self initialize];
}
return self;
}
- (void)initialize {
//请求网络数据
//这里才用的RACCommand方式,暂时先不说明
[self appendModelArray:@"Model解析的数据数组"];
}
#pragma mark - Private Methods
- (void)appendModelArray:(NSArray<TestModel *> *)array {
for (NSInteger i = 0; i < array.count; i++) {
TestCellViewModel *itemVM = [[TestCellViewModel alloc] initWithModel:array[i]];
if (itemVM) {
[self.mulDataSourceArr addObject:itemVM];
}
}
}
- (NSArray *)list {
return self.mulDataSourceArr.copy;
}
- (NSMutableArray<TestCellViewModel *> *)mulDataSourceArr{
if (!_mulDataSourceArr) {
_mulDataSourceArr = [[NSMutableArray<TestCellViewModel *> alloc] initWithCapacity:10];
}
return _mulDataSourceArr;
}
@end
CellViewModel:
.h
#import "TestModel.h"
@interface DYMineRegistrationCellViewModel : NSObject
///
@property (nonatomic, copy, readonly) NSAttributedString *formattedTitle;
@property (nonatomic, copy, readonly) NSString *formattedText;
.m
- (instancetype)initWithModel:(id)model {
NSAssert([model isKindOfClass:[DYMineRegistrationModel class]], @"model必须为DYMineRegistrationModel类型");
if (self = [super init]) {
self.itemModel = model;
}
return self;
}
#pragma mark - Getter Methods
- (NSAttributedString *)formattedtitle{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ ",self.itemModel.enregisterShare] attributes:@{NSFontAttributeName: [UIFont fontWithName:@"DINAlternate-Bold" size:15], NSForegroundColorAttributeName: UIColorHex(0x333333)}];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:@"哈哈哈)" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:10 weight:UIFontWeightRegular], NSForegroundColorAttributeName: UIColorHex(0xA3A3A3)}]];
return attributedString.copy;
}
- (NSString *)formattedText {
return xxx;
}
cell:
.h
#import <UIKit/UIKit.h>
@class TestCellViewModel;
@interface TestCell : UITableViewCell
/**
渲染数据
@param viewModel VM
*/
- (void)renderWithViewModel:(TestCellViewModel *)viewModel;
@end
.m
#import "TestCell.h"
#import "TestCellViewModel.h"
@implementation TestCell
- (void)renderWithViewModel:(DYMineRegistrationCellViewModel *)viewModel{
_viewModel = viewModel;
self.nameLabel.text = viewModel.formattedTitle;
self.textLabel.attributedText = viewModel.formattedText;
}
@end
我理解的基本流程如上,希望各位大神多多指点。