ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

iOS架构之MVC+MVVM

2022-06-27 21:00:07  阅读:244  来源: 互联网

标签:MVVM self viewModel iOS isFollow MVC View void view


一、简单了解MVC

经典图如下:M-Model;V- view;C-controller;就是Controller对象拥有View和Model对象,两者通过Controller进⾏沟通。在MVC中,controller都是挑大头的存在,网络请求的接收和处理都放在了Controller中,Model只负责了一下属性的定义;View则是独立的一块,但大多数手写代码的人会选择在loadview中加载view,而不会单独的在写一个view类来专门的写view,而stroryBoard和Xib其实很好的分离了view,但是由于其合并难的特点,在多人协作的场景下其实并不适用。因此大多数的人会将一些view的操作(比如判断密码是否合法等)都放在controller中实现,其实这并不是MVC设计的初衷,仅仅只是我们在写代码逻辑的时候没没有很好的区分,从而使得Controller有了上帝视角,什么都能干,但却看不出其中MVC真正的架构本质。

1.1 MVC的缺点

  • Controlller过重,C需要做的事情太多, 比如view的代理,数据的请求,业务逻辑,视图约束等等都可能会被写在C总
  • 耦合性高:view对model强引用,在view中也会对model进行操作,controlller也可以引用view,controller中也会对view做布局约束

1.2 MVC的优点

  • 简单,是比较容易上手写的一个架构模式,特别是在结合storyboard和xib使用之后
  • controller有绝对的上帝视角,既是优点也是缺点
  • 代码量少,当然这点是相对于MVVM来说的,因为他不需要额外的新建ViewModel类等,所以在一定程度上也减少了代码量

其实MVC最难受的地方就是在于controlller的权利过大,代码冗余,因此又出现了MVVM模式

二、MVVM

下面的这张图也是老朋友了,应该经常见:M-model数据层;VM-viewModel(介于view和model之间的中间层:主要负责业务逻辑,网络请求);C-viewController;V-view(布局视图、约束试图);通过VM将view和model分离,涉及两者的操作都放在了VM层,有效的缓解了MVC中C的压力

2.1 代码讲解:(参考链接自https://juejin.cn/post/7110933653240152077)

实现需求:点击某个按钮时,将按钮状态置反,然后像接口发送网络请求post新的状态,如果接口成功则提示成功,如果接口请求失败,则还原初始状态。

2.1.1

1) View.h:view对viewModel强引用

@interface OSArchitectureCell : UITableViewCell
@property (nonatomic, strong) OSAuthorViewModel *viewModel;
@end

2)view.m : 只对视图做布局;网络请求交给viewModel

//view 绑定视图
- (void)setViewModel:(OSAuthorViewModel *)viewModel {
    _viewModel = viewModel;
    //头像
    self.authorIcon.image = [UIImage imageNamed:viewModel.avater];
    //名字
    self.authorNameLabel.text = viewModel.name;
    // 是否关注
    [self updateFollowBtn:viewModel.isFollow];
    __weak typeof(self) weakSelf = self;
     //关注回调
    [viewModel setRefreshFollowState:^(BOOL isFollow) {
        [weakSelf updateFollowBtn:isFollow];
    }];
}

//按钮样式改变
- (void)updateFollowBtn:(BOOL)isFollow {
    if (isFollow) {
        _followBtn.backgroundColor = [UIColor grayColor];
        [_followBtn setImage:nil forState:UIControlStateNormal];
        [_followBtn setTitle:@"取消关注" forState:UIControlStateNormal];
        [_followBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    } else {
        _followBtn.backgroundColor = [UIColor yellowColor];
        [_followBtn setImage:[UIImage imageNamed:@"icon_follow"] forState:UIControlStateNormal];
        [_followBtn setTitle:@"关注" forState:UIControlStateNormal];
        [_followBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    }
}

//按钮点击响应
- (void)followAction:(UIButton *)sender {
    // mvvm
    //视图更新
    [self updateFollowBtn:!self.viewModel.isFollow];
    //数据改变,网络请求
    [self.viewModel requestFollow];
}

2.1.2 

1)viewModel.h : 采用block的方式进行通信

@interface OSAuthorViewModel : NSObject

@property (nonatomic, copy) NSString *avater;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) BOOL isFollow;

- (void)requestFollow;
@property (nonatomic, copy) void(^refreshFollowState)(BOOL isFollow);
@end

2) viewModel.m:做网络请求操作

- (void)requestFollow {
    // 改变数据
    self.isFollow = !self.isFollow;
    
    //网络请求
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            // 模拟失败
            if (arc4random_uniform(20) > 15) {
                if (self.refreshFollowState) {
                    self.refreshFollowState(!self.isFollow);
                }
                //模拟toast
                NSLog(@"%@失败",self.isFollow ? @"关注": @"取消关注");
                self.isFollow = !self.isFollow;
            } else {
                if (self.refreshFollowState) {
                    self.refreshFollowState(self.isFollow);
                }
                NSLog(@"%@成功",self.isFollow ? @"关注": @"取消关注");
            }
        });
    });
}

2.1.3 Model:

这里需要设置相应的model,然后在改变数据的时候就需要改变Model的数据。

ViewModel跟View的通信使用的是Block的方式。也就是说,如果视图引起的model改变,因为View对ViewModel 有引用,ViewModel对Model有引用,所以可以直接通过 [self.viewModel xxx]这种方式调用。而Model的改变要通信到View层需要通过ViewModel的Block(refreshFollowState)调用达到目的。而viewModel的block是在bindViewModel的时候赋值的,在赋值的时候一定要注意循环引用的问题。

2.1.4 cell的重用逻辑

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    OSArchitectureCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([OSArchitectureCell class]) forIndexPath:indexPath];
    
    OSAuthorViewModel *viewModel = self.vm.authorViewModels[indexPath.row];
    //绑定ViewModel
    cell.viewModel = viewModel;
    return cell;
}

 

三、MVP(面向协议编程)

在MVP中,P是present层,用来处理业务逻辑,M依然是数据层,V则是view和viewController的结合体。如下所示:

3.1 代码展示(参考https://juejin.cn/post/7110933653240152077)

3.1.1 view

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view = self.contentView;
    self.presenter = [[OSAuthorPresenter alloc] init];
    self.presenter.delegate = self;
    
}

- (void)reloadView {
    [self.contentView reloadData];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    OSAuthorMvpCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([OSAuthorMvpCell class]) forIndexPath:indexPath];
    OSAuthorModel *model = self.presenter.dataList[indexPath.row];
    cell.authorIcon.image = [UIImage imageNamed:model.avater];
    cell.authorNameLabel.text = model.name;
    cell.indexPath = indexPath;
    [cell updateFollowBtn:model.isFollow];
    return cell;
}

3.1.2 

1)present.h(presenter和view的交互通过代理来实现双向通信;P则通过请求数据赋值给Model来实现P和M间的通信)

@protocol OSAuthorPresenter <NSObject>
@optional
- (void)didClickFollow:(BOOL *)isFollow indexpath:(NSIndexPath *)indexpath;
- (void)reloadView;
@end

2) present.m

@implementation OSAuthorPresenter
- (void)loadData {
    // 模拟网络请求
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            OSAuthorModel *author1 = [[OSAuthorModel alloc] init];
            author1.avater = @"avatar-1";
            author1.name = @"小红";
            author1.isFollow= YES;
            OSAuthorModel *author2 = [[OSAuthorModel alloc] init];
            author2.avater = @"avatar-2";
            author2.name = @"小蓝";
            author2.isFollow= NO;
            OSAuthorModel *author3 = [[OSAuthorModel alloc] init];
            author3.avater = @"avatar-3";
            author3.name = @"小绿";
            author3.isFollow= YES;
            self.dataList = @[author1, author2, author3];
            if ([self.delegate respondsToSelector:@selector(reloadView)]) {
                [self.delegate reloadView];
            }
        });
    });
}

- (void)didClickFollow:(BOOL *)isFollow indexpath:(NSIndexPath *)indexpath {
    //..code..
}
@end

3)cell

- (void)followAction:(UIButton *)sender {
    if ([self.delegate respondsToSelector:@selector(didClickFollow:indexpath:)]) {
        [self.delegate didClickFollow:self.isFollow indexpath:self.indexPath];
    }
}

3.2 MVP的好处

  • 是一种适用于所有程序的架构模式
  • Presenter层作为连接View层与Model层的桥梁,使得在MVP架构中Model与View无法直接进行交互。Presenter层会从Model层获得所需要的数据,进行一些适当的处理后交由View层进行显示,这样通过Presenter将View与Model进行隔离,使得View和Model之间不存在耦合,同时也将业务逻辑从View中抽离。
  • Presenter与View的交互式通过接口来实现的,耦合度低,也有利于单元测试
  • Presenter是基于行为的,一个Presnter可用于多个View,增强了代码复用

四、架构的意义

      架构的意义在于让项目结构清晰,更容易维护,迭代起来更加轻松。架构模式之间没有三六九等,只有项目适合于某种架构模式,并不存在某种架构模式比另一种架构模式更加优秀,像是平时写的一个静态页面,如果采用MVC写,一个文件一个类就可以完全搞定,如果非要使用MVVM,MVP,反而多增加几个类,在项目优化的时候,我们又在绞尽脑汁得减少类的使用,索性在这时候就放弃项目整体的架构,转而使用更简单的架构也是一种不错的选择。

标签:MVVM,self,viewModel,iOS,isFollow,MVC,View,void,view
来源: https://www.cnblogs.com/SNMX/p/16417505.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有