从MVP到MVVM

栏目: IOS · 发布时间: 7年前

内容简介:通过上篇文章,我们了解到利用mvp可以对mvc的c层瘦身,并使得层次分明,且网络请求变得通用。那就已经很完美了,那么mvvm是用来干嘛的呢。对于mvvm它是用来描述数据和视图的关系的。我们开发过程中常常发现数据变化会导致视图的变化,如一个列表,当没有数据的时候要展示空界面,当有数据的时候要展示对应条数的列表界面。在比如当网络请求回来的列表中的一个为已点赞数据,那么界面的相应视图就要显示红心其他都为灰心。以上示例都反应了数据和视图的这种绑定关系,并且数据一变化视图就要相应的进行变化,也就是所谓的数据驱动。

通过上篇文章,我们了解到利用mvp可以对mvc的c层瘦身,并使得层次分明,且网络请求变得通用。那就已经很完美了,那么mvvm是用来干嘛的呢。

对于mvvm它是用来描述数据和视图的关系的。我们开发过程中常常发现数据变化会导致视图的变化,如一个列表,当没有数据的时候要展示空界面,当有数据的时候要展示对应条数的列表界面。在比如当网络请求回来的列表中的一个为已点赞数据,那么界面的相应视图就要显示红心其他都为灰心。

以上示例都反应了数据和视图的这种绑定关系,并且数据一变化视图就要相应的进行变化,也就是所谓的数据驱动。

以上介绍在说明数据驱动视图这件事,这和mvc,mvp是不同的,导致了编码思想也不同。

数据一变化视图就要变化,也就是要监听数据的变化,这可以利用kvo。但是苹果中的kvo用起来很麻烦,首先要注册kvo,并在界面销毁时候要释放kvo。而RAC的出现解决了这个麻烦,从而使得mvvm的写法变得更方便了。也就是说mvvm要用到kvo思想来监听数据源的变化,从而让视图变化。rac代替了kvo的这种繁琐写法。mvvm用的是kvo思想,rac不是必须的,但是用了更方便而已。关于rac的用发可以参考这里

MVVM

M:Model,它是模型类,就是有很多属性,每个属性名字和后台返回的字段名相同。

V:View,它是视图类,如继承与UIView类的所有类都可以认为是V层,并且Controller也属于V层,即继承与UIView,UIViewController类的所有类都属于V层。这个类里存放的都是和视图相关的逻辑代码。并且controller类里存放的是一些胶水代码(就是起到粘合作用的代码)。和p层有区别,加入了视图和数据源的绑定机制。

VM:viewModel,它是vm层,这里是主要写网络请求,数据逻辑的地方。处理和视图无关的数据逻辑等。这个类不该持有v层,也就是不该#import <UIKit/UIKit.h>。并且该类和p层是有区别的,它不是通过回调的方式返回到v层。而是通过kvo的方式。

三者关系是,v层持有vm层,vm层持有m层,并通过kvo的方式通知v层。

通过以上分析,其实mvvm设计模式和mvp很像。只不过是把p层中的网络请求的回调去掉了,通过kvo的方式返回到v层。并把视图和vm层进行绑定,将p层进行这两个修改后重新起一个名字叫做vm层,也就变成了mvvm了。

下边我们用一些伪代码来进一步探索MVVM的应用,其实和MVP一样,我们也需要封装出一套MVVM的基类。用这些基类可以使得我们编码更方便。

从MVP到MVVM
//ArticleListModel.h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ArticleListModel : NSObject

/** 文章内容 */
@property (copy, nonatomic) NSString *content;
/** 点赞否 */
@property (assign, nonatomic) NSInteger praise;
@end

NS_ASSUME_NONNULL_END

//ArticleListModel.m文件
#import "ArticleListModel.h"

@implementation ArticleListModel

@end

//ArticleListVC.h文件
#import "BSMvpUtilsBaseViewController.h"

NS_ASSUME_NONNULL_BEGIN

@interface ArticleListVC : BSMvpUtilsBaseViewController

@end

NS_ASSUME_NONNULL_END

//ArticleListVC.m文件
#import "ArticleListVC.h"
#import "ArticleListVM.h"

@interface ArticleListVC ()
@property (strong, nonatomic) ArticleListVM *vm;
@property (strong, nonatomic) UITableView *tableView;
@end

@implementation ArticleListVC

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //v层持有vm层
    self.vm = [[ArticleListVM alloc] init];
    //初始化视图
    [self setUpUI];
    //视图与数据源绑定
    [self bindView];
    //网络请求
    [self.vm fetchData];
}
- (void)setUpUI{
    //tableView的布局等视图初始化代码
}

- (void)bindView{
    @weakify(self);
    [[RACObserve(self.vm, error) skip:1] subscribeNext:^(id  _Nullable x) {
        @strongify(self);
        if (self.vm.error) {
            //如果有错,弹出错误Toast等
        }
    }];
    
    [[RACObserve(self.vm, arrList) skip:1] subscribeNext:^(id  _Nullable x) {
        @strongify(self);
        if (self.vm.arrList.count == 0) {
            //显示无数据占位图等
        }else{
            //隐藏无数据占位图等
        }
        //tableView刷新数据源
        [self.tableView reloadData];
    }];
}

//列表中的某一个点赞的心形被点击
- (void)cellPraiseBtnClick:(NSIndexPath *)indexPath{
    //根据indexPath找到对应的数据源,然后根据数据源的状态请求相应的接口,最后改变数据源的状态。
    [self.vm praiseMethod:indexPath.row];
}
@end

//ArticleListVM.h文件
#import "BSMvvmUtilsBaseVM.h"
@class ArticleListModel;
NS_ASSUME_NONNULL_BEGIN

@interface ArticleListVM : BSMvvmUtilsBaseVM
/** 出错 */
@property (strong, nonatomic) NSError *error;
/** 点赞列表数据源 */
@property (copy, nonatomic) NSArray<ArticleListModel *> *arrList;
- (void)fetchData;


/**
 点赞和取消点赞方法

 @param index index
 */
- (void)praiseMethod:(NSInteger)index;

/**
 点赞,取消点赞请求

 @param isPraise 1:点在 0:取消点赞
 */
- (void)fetchPraise:(NSNumber *)isPraise;
@end

NS_ASSUME_NONNULL_END



//ArticleListVM.m文件
#import "ArticleListVM.h"
#import "ArticleListModel.h"
#import <BSUtilsBaseRequestManager.h>

@implementation ArticleListVM

- (instancetype)init{
    self = [super init];
    if (self) {
        _error = nil;
        _arrList = nil;
    }
    
    return self;
}

//网络请求
- (void)fetchData{
    [[BSUtilsBaseRequestManager new] postWithUrl:@"xxx" parmas:@{} finishBlk:^(NSDictionary * _Nonnull responseObject, NSError * _Nonnull error) {
        if (error) {
            self.error = error;
        }else{
            //数据转模型,并把转换的结果数据赋值给arrList
            self.arrList = @[];
        }
    }];
}

//点赞和取消点赞方法
- (void)praiseMethod:(NSInteger)index{
    NSMutableArray *arrTemp = [NSMutableArray arrayWithArray:self.arrList];
    ArticleListModel *model = self.arrList[index];
    NSInteger isPraise = model.praise;
    if (isPraise) {
        //当前是已经点过攒了
        //下边要请求取消点赞接口,并判断是否请求成功
        if(!self.error){
            //取消点赞成功
            model.praise = 0;
            [arrTemp replaceObjectAtIndex:index withObject:model];
            self.arrList = arrTemp;
        }
    }else{
        //当前是还没有点过攒
        //下边要请求点赞接口,并判断是否请求成功
        if(!self.error){
            //点赞成功
            model.praise = 1;
            [arrTemp replaceObjectAtIndex:index withObject:model];
            self.arrList = arrTemp;
        }
    }
   
}

//点赞,取消点赞请求
- (void)fetchPraise:(NSNumber *)isPraise{
    if(isPraise){
        [[BSUtilsBaseRequestManager new] postWithUrl:@"xxx" parmas:@{@"praise":isPraise} finishBlk:^(NSDictionary * _Nonnull responseObject, NSError * _Nonnull error) {
            self.error = error;
        }];
    }
}
@end
复制代码

总结

通过以上分析我们知道了mvvm是要把vm层和v层进行绑定的,然后我们只要改变vm层的数据源即可改变视图。这是关键。首先进行数据源和视图的绑定再改变数据源。

其实所谓的vm层和v层绑定其实就是监听vm层的数据源变化从而改变视图罢了。这种绑定利用RAC会更加方便。当然了不用RAC也可以的只是麻烦些而已。

总是要记住首先vm层和视图层的绑定,之后只要操作vm层的数据源即可。比如上述的代码我要显示列表界面,首先监听vm层的arrList,只要有变化就进入回调,在回调中判断是否要显示空界面还是具体的列表界面。这就是所谓的绑定。之后我们网络请求,如果请求成功就对arrList赋值,这个过程就是在改变vm层的数据源,从而使得视图更新。

在比如我们要对列表中的某个心形进行点击(这时候心形有两种状态的),进行点赞或者取消点赞。这时候我们根据index找到对应的数据源,进行网络请求,在把对应的数据源praise状态改变重新赋值给vm层的arrList属性即可,因为之前arrList数据源在和视图绑定着呢。所以这时候改变arrList从而更新了视图。

当你用了mvvm这种编码方式时候,可以感觉到减少了刷新代码,不必每次改变完数据后都要手动去写刷新视图的代码了。原因是绑定只要绑定一次即可,就在不停的监听数据的变化了。其实不仅仅是减少了刷新视图的代码,同时编码方式变了,思想变了。变成了数据驱动视图了。至于mvp好还是mvvm好,我个人觉得mvvm这种思想还是挺有趣的,同时这种数据驱动视图确实是普遍现象。而且这是一种新的有趣的编码方式。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Host Your Web Site In The Cloud

Host Your Web Site In The Cloud

Jeff Barr / SitePoint / 2010-9-28 / USD 39.95

Host Your Web Site On The Cloud is the OFFICIAL step-by-step guide to this revolutionary approach to hosting and managing your websites and applications, authored by Amazon's very own Jeffrey Barr. "H......一起来看看 《Host Your Web Site In The Cloud》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具