一、简单了解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 代码讲解:
实现需求:点击某个按钮时,将按钮状态置反,然后像接口发送网络请求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 代码展示
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,反而多增加几个类,在项目优化的时候,我们又在绞尽脑汁得减少类的使用,索性在这时候就放弃项目整体的架构,转而使用更简单的架构也是一种不错的选择。