iOS之MVVM架构

来源:互联网 发布:ubuntu chrome 安装 编辑:程序博客网 时间:2024/04/28 04:56

什么是MVVM


MVVM就是在MVC的基础上分离出业务处理的逻辑到viewModel层,即:
model层,API请求的原始数据

view层,视图展示,由viewController来控制


viewModel层,负责业务处理和数据转化,简单来说,就是API请求完数据,解析成model,之后在viewModel中转化成能够直接被视图层使用的数据,交付给展示页面。



Model层
是少不了的了,我们得有东西充当DTO(数据传输对象),当然,用字典也是可以的,编程么,要灵活一些。Model层是比较薄的一层,如果学过Java的小伙伴的话,对JavaBean应该不陌生吧。

ViewModel层,就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。

View层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。

view-model 一词的确不能充分表达我们的意图. 一个更好的术语可能是 “View Coordinator”(感谢Dave Lee提的这个 “View Coordinator” 术语, 真是个好点子)。你可以认为它就像是电视新闻主播背后的研究人员和作家团队。它从必要的资源(数据库, 网络服务调用, 等)中获取原始数据, 运用逻辑, 并处理成 view (controller) 的展示数据. 它(通常通过属性)暴露给视图控制器需要知道的仅关于显示视图工作的信息(理想地你不会暴漏你的 data-model 对象)。 它还负责对上游数据的修改(比如更新模型/数据库, API POST 调用)。

MVVM是基于胖Model的架构思路建立的,然后在胖Model中拆出两部分:Model和ViewModel。ViewModel本质上算是Model层(因为是胖Model里面分出来的一部分),所以View并不适合直接持有ViewModel,因为ViewModel有可能并不是只服务于特定的一个View,使用更加松散的绑定关系能够降低ViewModel和View之间的耦合度。


  • ViewController 尽量不涉及业务逻辑,让 ViewModel 去做这些事情。
  • ViewController 只是一个中间人,接收 View 的事件、调用 ViewModel 的方法、响应 ViewModel 的变化。
  • ViewModel 不能包含 View,不然就跟 View 产生了耦合,不方便复用和测试。
  • ViewModel 之间可以有依赖。
  • ViewModel 避免过于臃肿,不然维护起来也是个问题。

MVVM 并不复杂,跟 MVC 也是兼容的,只是多了一个 ViewModel 层,但就是这么一个小改动,就能让代码变得更加容易阅读和维护,不妨试一下吧。


怎么实现MVVM呢接下来我将通过代码来演示一下我的具体实现过程。

model层

在这里我们用JSONModel来解析,比如一个商品列表的model长这样:
#import <JSONModel/JSONModel.h>@protocol ProductListModel <NSObject>@end@interface ProductListModel : JSONModel@property (nonatomic, copy) NSString *imgUrl;@property (nonatomic, copy) NSString *productId;@property (nonatomic, copy) NSString *productName;@property (nonatomic, copy) NSString *refPrice;@end

 这是我们的数据原型,API返回的数据通过JSONModel解析完成后的原始数据存在这里。


viewModel 层 

viewModel层是我们处理业务逻辑的核心层,在这里我们需要发起网络请求(如果网络请求较多,可以抽出来,只在ViewModel里调用)、解析数据、转换数据给前端。

- (void)startLoadProductListWithPage:(NSInteger)page {  __weak typeof(self) weakSelf = self;  [NetWorkManager GET:self.baseURL                   parameters:parameters                        success:^(NSURLSessionDataTask *task, id responseObject) {    __strong typeof(weakSelf) strongSelf = weakSelf;    ...    NSDictionary *resultDic = responseObject[@"rp_result"];    NSError *error = nil;    ProductListModel *model = [[ProductListModel alloc] initWithDictionary:resultDic error:&error];     if (error) {      ...    }    [strongSelf calProductLists:model.productlist];    if (strangles.delegate ...) {       ...    }  } failure:^(NSURLSessionDataTask *task, NSError *error) {    ...  }];}- (void)calProductLists:(NSArray *)productLists {  for (NSInteger i = 0; i < productLists.count; ++i) {    ProductListModel *model = productLists[i];    ProductListItem *item = [[ProductListItem alloc] init];    item.productId = model.productId;    item.productName = model.productName;    item.productPrice = [NSString stringWithFormat:@"¥ %@", model.refPrice];    item.productImgURL = [Utils convertToRealUrl:model.imgUrl ofsize:300];    [self.productLists addObject:item];  }}

在viewModel中将API返回的数据解析成model,并将model转化成可供view层直接使用的item,将item交付给前端使用。

经过viewModel转化之后的数据item由viewModel保存,与数据相关的处理都将在viewModel中处理。viewModel返回给view层的接口长这样:
@interface ProductListViewModel (CollectionViewDataSource)- (NSInteger)m_numberOfItemsInSection:(NSInteger)section;- (ProductListItem *)m_itemForIndexPath:(NSIndexPath *)indexPath;@end


view层

view层是由viewController控制的。view层只做展示,不做业务处理。view层的数据由viewModel提供。view层看起来是这样的:

@implementation ProductListViewController- (void)viewDidLoad {  [super viewDidLoad];  self.view.backgroundColor = [UIColor whiteColor];  [self initial];  [self setupViewModel];  [self setupSubViews];  [self.viewModel startLoadProductListWithPage:_currentPage];}- (void)initial {  ...  self.currentPage = 1;}- (void)setupViewModel {  self.viewModel = [[ProductListViewModel alloc] init];  _viewModel.delegate = self;}- (void)setupSubViews {  ...  [self setupCollectionView];  ...}- (void)setupCollectionView {   ...}#pragma mark - UICollectionView Delegate & Datosource- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {  return [self.viewModel m_numberOfItemsInSection:section];}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {  ProductListItem *item = [self.viewModel m_itemForIndexPath:indexPath];  ProductListCollectionViewCell *cell = (ProductListCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kProductListCollectionViewCellId forIndexPath:indexPath];  [cell setupWithItem:item];  return cell;}


MVVM的缺点

转化过程的成本很大,主要在于:

1、数组内容的转化成本较高:数组里面每项都要转化成Item对象,如果Item对象中还有类似数组,就很头疼。
2、转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需要第二次转化。
3、只有在API返回的数据高度标准化时,这些对象原型(Item)的可复用程度才高,否则容易出现类型爆炸,提高维护成本。
4、调试时通过对象原型查看数据内容不如直接通过NSDictionary/NSArray直观。
5、同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方。
针对这些缺点,也提出了相应的解决方法,即用一个类似reformer的对象进行数据过滤,根据不同的reformer对象过滤出不同的数据。


另外,这篇文章说得也很浅显易懂!