一、简单了解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,反而多增加几个类,在项目优化的时候,我们又在绞尽脑汁得减少类的使用,索性在这时候就放弃项目整体的架构,转而使用更简单的架构也是一种不错的选择。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/270556.html