美文网首页MVVM+RAC
MVVM简单的实例演示,一看就懂

MVVM简单的实例演示,一看就懂

作者: 糖糖本尊 | 来源:发表于2017-03-23 17:36 被阅读1643次

小酌下MVVM

  • 在这呢也不赘述什么是MVC,神马又是MVVM了,在百度上谷歌一下一抓一大把,在这儿就简单的提上一嘴。下面的Demo用的就是MVVM的架构模式。附上连接https://github.com/lanyuzx/iOS-Demo-
    Model层是少不了的了,我们得有东西充当DTO(数据传输对象),当然,用字典也是可以的,编程么,要灵活一些。Model层是比较薄的一层。
  • ViewModel层,就是View和Model层的粘合剂,他是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他各种各样的代码的极好的地方。说白了,就是把原来ViewController层的业务逻辑和页面逻辑等剥离出来放到ViewModel层。
  • View层,就是ViewController层,他的任务就是从ViewModel层获取数据,然后显示。
CC81458C-EDB8-4B78-9F50-7B78D89A7BC3.png

不扯犊子,直接进入话题

该demo,是使用开发中常用的tabView作为例子演示的,简单移动
首先我们看控制器代码如下
@interface LLMVVMViewController ()
//自定义一个tabView
@property (nonatomic,strong)  LLMVVMTabView * tabView;
//记录下拉刷新的索引
@property (nonatomic,assign)  NSInteger  pageIndex;
@end
@implementation LLMVVMViewController
  /// MARK: ---- 试图控制器
> -(void)viewDidLoad {
    [super viewDidLoad];
     self.pageIndex = 1;
    [self.view addSubview:self.tabView];
    [self setupData];
}
-(void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
-(void)dealloc {
    NSLog(@"%s",__func__);
    NSLog(@"***********------------*************");
    NSLog(@"*******************MVVM使用很多的回掉方法,一定要注意循环引用问题*********************");
    NSLog(@"***********------------*************");
}
  /// MARK: ---- 请求网络
-(void)setupData {
    __weak typeof(self) weak = self;
   [[LLMVVMViewModel shareViewModel]setupRequsetDate:self.pageIndex :^(id sucess) {
       weak.tabView.modelArr = sucess;
       [weak.tabView.mj_header endRefreshing];
       [weak.tabView.mj_footer endRefreshing];
   } :^(id error) { 
   }];
}
   /// MARK: ---- 懒加载
-(LLMVVMTabView *)tabView {
         if (_tabView == nil) {
        _tabView = [[LLMVVMTabView alloc]initWithFrame:CGRectMake(0, 0, LLScreenW, LLScreenH ) style:UITableViewStylePlain];
             __weak typeof(self) weak = self;
        //tabViewcell的点击跳转方法
       _tabView.block = ^(LLMVVMModel * model) {
            [[LLMVVMViewModel shareViewModel]movieDetailWithPublicModel:model WithViewController:weak];
        };
             //下拉刷新方法
             _tabView.blockHeader = ^(NSInteger pageNum) {
                 weak.pageIndex = pageNum;
                 [weak setupData];
             };
             //上拉加载的回掉方法
             _tabView.blockFooter = ^(NSInteger pageNum) {
                 weak.pageIndex = pageNum;
                 [weak setupData];
             };
           }
    return _tabView;
}

首页就是在程序启动时间,试图加载完成后进行了网络请求,并且添加了子控件,就干了这么多事,是不是感觉清晰了好多

接着我们看ViewModel代码.h如下
@interface LLMVVMViewModel : NSObject
//回调网络请求的模型数据
-(void)setupRequsetDate :(NSInteger)pageIndex :(SuccessBlock)successblock :(ErrorBlock) errorblock;
//创建该类的单例,防止重复创建
+(LLMVVMViewModel *)shareViewModel;
//跳转到电影详情页
- (void)movieDetailWithPublicModel: (LLMVVMModel *)movieModel WithViewController: (UIViewController *)superController;
@end
接着我们看ViewModel代码.m如下
@interface LLMVVMViewModel()
//创建一个数组,记录网络请求的数据
 @property (nonatomic,strong)  NSMutableArray * modelArr;
@end
@implementation LLMVVMViewModel

//创建一个单例,防止重复创建
+(LLMVVMViewModel *)shareViewModel{
    
    static LLMVVMViewModel * shareViewModel  ;
    static dispatch_once_t predicate;
    dispatch_once(&predicate,^{
        shareViewModel = [[self alloc]init];
    });
    return shareViewModel;
}

-(void)setupRequsetDate :(NSInteger)pageIndex :(SuccessBlock)successblock :(ErrorBlock) errorblock {
    

    //模拟异步加载数据 ,从本地去数据
    __weak typeof(self) weakSelf = self;
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSString * dataPath = [[NSBundle mainBundle]pathForResource:@"MVVM" ofType:nil];
        NSData * data = [NSData dataWithContentsOfFile:dataPath];
        NSDictionary * jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        
        NSArray * modelTemp = [LLMVVMModel BAMJParse:[jsonDict objectForKey:@"subjects"]];
        if (pageIndex == 1) {
            NSLog(@"下拉刷新");
            weakSelf.modelArr = [NSMutableArray arrayWithArray:modelTemp];
            
            }else {
            [weakSelf.modelArr addObjectsFromArray:weakSelf.modelArr];
                NSLog(@"上拉加载");
        }
        //回掉给请求者
        successblock( weakSelf.modelArr);
    });
    
}
//这里是点击cell跳转的方法,在MVC中交给控制器处理的,在MVVM中交给ViewModel处理
-(void)movieDetailWithPublicModel:(LLMVVMModel *)movieModel WithViewController:(UIViewController *)superController {
    MovieViewController *movieVC = [[MovieViewController alloc] init];
  LLMVVMDetailModel * model =  movieModel.casts.firstObject;
   movieVC.url = model.alt;
    [superController.navigationController pushViewController:movieVC animated:YES];

}
-(NSMutableArray *)modelArr {
    
    if (_modelArr == nil ) {
        _modelArr = [NSMutableArray array];
    }
    return _modelArr;
}

在ViewModel中,我们处理了数据的解析,以及上下拉刷新的数据处理,回掉给控制器,再有控制器的子试图接收数据,刷新表格
接着我们继续看 在试图tabView中代码
@class LLMVVMModel;
//定义每个cell点击的block
typedef void(^MVVMTabViewBlock)(LLMVVMModel*);
//上下拉刷新的页码传递
typedef void(^refreshHeaderBlock)(NSInteger pageNum);
typedef void(^refreshFooterBlock)(NSInteger pageNum);

@interface LLMVVMTabView : UITableView
//模型数组
@property (nonatomic,strong)  NSArray * modelArr;

@property (nonatomic,copy)  MVVMTabViewBlock  block;

@property (nonatomic,copy)  refreshHeaderBlock blockHeader ;
@property (nonatomic,copy)  refreshHeaderBlock  blockFooter;


@end
@interface LLMVVMTabView()<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic,assign)  NSInteger  pageIndex;
@end


@implementation LLMVVMTabView
//从写模型的Set方法,获取新的数据
-(void)setModelArr:(NSArray *)modelArr {
    
    _modelArr = modelArr;
    
    [self reloadData];

}
//构造方法
-(instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
    if (self = [super initWithFrame:frame style:style]) {
        
        self.dataSource = self;
        self.delegate = self;
        
        [self  registerClass:[LLMVVMTableViewCell class] forCellReuseIdentifier:@"LLMVVMTableViewCell"];
         __weak typeof(self) weak = self;
//下拉刷新
        self.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
            weak.pageIndex = 1;
//回掉页码数
            weak.blockHeader(weak.pageIndex);
        }];
//上拉刷新
        self.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
            weak.pageIndex++;
//回掉页码数
             weak.blockFooter(weak.pageIndex);
           
        }];

    }
    return self;
}

/// MARK: ---- 数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return self.modelArr.count;
    
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    LLMVVMTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"LLMVVMTableViewCell"];
    LLMVVMModel * model = self.modelArr[indexPath.row];
    cell.model = model;
    return cell;
    
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    return 100;

}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    //点击毁掉给控制器,再有控制器传递给ViewModel
    self.block(self.modelArr[indexPath.row]);
    
    
}

MVVM的缺点

  • 这种做法是能够提高后续操作代码的可读性的。在比较直觉的思路里面,是需要这部分转化过程的,但这部分转化过程的成本是很大的,主要成本在于:

  • 数组内容的转化成本较高:数组里面每项都要转化成Item对象,如果Item对象中还有类似数组,就很头疼。

  • 转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需要第二次转化。

  • 只有在API返回的数据高度标准化时,这些对象原型(Item)的可复用程度才高,否则容易出现类型爆炸,提高维护成本。

  • 调试时通过对象原型查看数据内容不如直接通过NSDictionary/NSArray直观。

  • 同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方

相关文章

网友评论

    本文标题:MVVM简单的实例演示,一看就懂

    本文链接:https://www.haomeiwen.com/subject/fedtottx.html