MVVM简要

2021-04-19  本文已影响0人  大成小栈

MVVM(M V VM,即Model、ViewController/View、ViewModel)可以看成是在MVC模式上的更进一步的封装。MVVM更注重解耦,但也增加了代码量,这样带来的好处是代码结构分层更细,模块功能更具体,方便测试。


MVVM交互图 以TableView为例的MVVM架构实现

根据以上两图的层次对应,其中,V包括了ViewController/View,ViewModel包括了TableViewModel/TableViewDataSource/TableViewDelegate。其实,ViewModel就是对数据处理逻辑的剥离,将数据处理逻辑从MVC中的Controller中独立出来,从而Controller中就只留一些简单的交互。例如,网络请求、从数据库中获取数据等均可以置于ViewModel中(对于UITabelView来说,得益于UITableViewDataSource、UITableViewDelegate这两个代理,使得tabelView内容的更新,也可以独立出来,做为ViewModel的一部分。这一点,非常值得借鉴)。

在MVVM的结构中,要非常注意循环引用的发生,适时使用weak。

以UITabelView为例,的MVVM代码:

Model:
//// CustomModel.h
#import <Foundation/Foundation.h>

@interface CustomModel : NSObject
@property (nonatomic,strong) NSString *title;
@end

////CustomModel. m
#import "CustomModel.h"

@implementation CustomModel
@end
ViewController/View:
//// CustomTableViewCell.h
#import <UIKit/UIKit.h>

@interface CustomTableViewCell : UITableViewCell
@property(nonatomic,strong) UILabel *titleLabel;
@end

////CustomTableViewCell. m
#import "CustomTableViewCell.h"

@implementation CustomTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
        _titleLabel=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, WScreen, 50)];
        [self.contentView addSubview:_titleLabel];
        _titleLabel.backgroundColor=[UIColor whiteColor];
        _titleLabel.font=[UIFont systemFontOfSize:14];
       
    }
    return self;
}

@end

==================================================

////TableViewController. h
#import <UIKit/UIKit.h>
@interface TableViewController : UIViewController

@end

////TableViewController. m
#import "TableViewController.h"
#import "YiRefreshHeader.h"
#import "YiRefreshFooter.h"
#import "TableViewModel.h"
#import "TableViewDataSource.h"
#import "TableViewDelegate.h"
@interface TableViewController () {

    YiRefreshHeader *refreshHeader;
    YiRefreshFooter *refreshFooter;
    NSMutableArray *totalSource;
    TableViewModel *tableViewModel;
    UITableView *tableView;
    TableViewDataSource *tableViewDataSource;
    TableViewDelegate *tableViewDelegate;
}

@end

@implementation TableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    if (iOS7) {
        self.edgesForExtendedLayout = UIRectEdgeBottom | UIRectEdgeLeft | UIRectEdgeRight;
    }
    self.title=@"MVVMDemo With TableView";
    self.view.backgroundColor=[UIColor whiteColor];
    
    tableView=[[UITableView alloc] initWithFrame:CGRectMake(0, 0, WScreen, HScreen-64) style:UITableViewStylePlain];
    [self.view addSubview:tableView];
    tableViewDataSource=[[TableViewDataSource alloc] init];
    tableViewDelegate=[[TableViewDelegate alloc] init];
    tableView.dataSource=tableViewDataSource;
    tableView.delegate=tableViewDelegate;
    tableViewModel=[[TableViewModel alloc] init];
    totalSource=0;
    
//    YiRefreshHeader  头部刷新按钮的使用
    refreshHeader=[[YiRefreshHeader alloc] init];
    refreshHeader.scrollView=tableView;
    [refreshHeader header];
    __weak typeof(self) weakSelf = self;
    refreshHeader.beginRefreshingBlock=^(){
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf headerRefreshAction];
    };
    
//    是否在进入该界面的时候就开始进入刷新状态
    [refreshHeader beginRefreshing];

//    YiRefreshFooter  底部刷新按钮的使用
    refreshFooter=[[YiRefreshFooter alloc] init];
    refreshFooter.scrollView=tableView;
    [refreshFooter footer];
    
    refreshFooter.beginRefreshingBlock=^(){
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf footerRefreshAction];
    };
    
}

- (void)headerRefreshAction {
   
    [tableViewModel headerRefreshRequestWithCallback:^(NSArray *array){
        totalSource=(NSMutableArray *)array;
        tableViewDataSource.array=totalSource;
        tableViewDelegate.array=totalSource;
        [refreshHeader endRefreshing];
        [tableView reloadData];
    }];

}

- (void)footerRefreshAction {
    [tableViewModel footerRefreshRequestWithCallback:^(NSArray *array){
        [totalSource addObjectsFromArray:array] ;
        tableViewDataSource.array=totalSource;
        tableViewDelegate.array=totalSource;
        [refreshFooter endRefreshing];
        [tableView reloadData];
    
    }];
  
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
ViewModel
////TableViewModel.h
#import <Foundation/Foundation.h>

typedef void (^callback) (NSArray *array);

@interface TableViewModel : NSObject

//tableView头部刷新的网络请求
- (void)headerRefreshRequestWithCallback:(callback)callback;
//tableView底部刷新的网络请求
- (void)footerRefreshRequestWithCallback:(callback)callback;

@end

////TableViewModel.m
#import "TableViewModel.h"
#import "CustomModel.h"
@interface TableViewModel ()

@end

@implementation TableViewModel

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

- (void)headerRefreshRequestWithCallback:(callback)callback {
        //  后台执行:
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_async(dispatch_get_main_queue(), ^{
                //               主线程刷新视图
                NSMutableArray *arr=[NSMutableArray array];
                for (int i=0; i<16; i++) {
                    int x = arc4random() % 100;
                    NSString *string=[NSString stringWithFormat:@"    (random%d) 君子性非异也,善假于物也!",x];
                    CustomModel *model=[[CustomModel alloc] init];
                    model.title=string;
                    [arr addObject:model];
                }
                callback(arr);
            });
        });
}

- (void )footerRefreshRequestWithCallback:(callback)callback {
        //  后台执行:
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            sleep(2);
            dispatch_async(dispatch_get_main_queue(), ^{
                //               主线程刷新视图
                NSMutableArray *arr=[NSMutableArray array];
                for (int i=0; i<16; i++) {
                    int x = arc4random() % 100;
                    NSString *string=[NSString stringWithFormat:@"    (random%d) 君子性非异也,善假于物也!",x];
                    CustomModel *model=[[CustomModel alloc] init];
                    model.title=string;
                    [arr addObject:model];
                }
                callback(arr);
            });
        });
}

@end

==================================================

//// TableViewDataSource.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface TableViewDataSource : NSObject<UITableViewDataSource>

@property (nonatomic,strong) NSArray *array;

@end

//// TableViewDataSource.m
#import "TableViewDataSource.h"
#import "CustomTableViewCell.h"

@implementation TableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _array.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.titleLabel.text=((CustomModel *)[_array objectAtIndex:indexPath.row]).title;
    return cell;
}

@end

==================================================

//// TableViewDelegate.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface TableViewDelegate : NSObject<UITableViewDelegate>
@property (nonatomic,strong) NSArray *array;
@end

//// TableViewDelegate.m
#import "TableViewDelegate.h"

@implementation TableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (_array.count>0) {
        UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"" message:((CustomModel *)[_array objectAtIndex:indexPath.row]).title delegate:nil cancelButtonTitle:@"sure" otherButtonTitles:nil, nil];
        [alert show];
    }    
}
@end

ReactiveCocoaRxSwift 等框架对MVVM有非常好的延伸和实现,学无止境!

参考文章:
https://github.com/coderyi/MVVMDemo
https://github.com/LXManMan/LXMVVMExample
https://teehanlax.com/blog/model-view-viewmodel-for-ios/

上一篇 下一篇

猜你喜欢

热点阅读