iOS架构之MVC+MVVM


一、简单了解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真正的架构本质。

iOS架构之MVC+MVVM

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的压力

iOS架构之MVC+MVVM

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的结合体。如下所示:
iOS架构之MVC+MVVM

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,反而多增加几个类,在项目优化的时候,我们又在绞尽脑汁得减少类的使用,索性在这时候就放弃项目整体的架构,转而使用更简单的架构也是一种不错的选择。

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/270556.html

(0)
上一篇 2022年6月27日
下一篇 2022年6月27日

相关推荐

发表回复

登录后才能评论