原文连接程序员
目前客户端最流行的架构应该就是MVVM,然而在看了一些文章以后发现大部分是理论而并无仔细讲解具体的架构方法和实践,这篇博客说说我在实际工做中的使用。
提到MVVM咱们不得不先来认识一下MVC:
MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。MVC模式最先由Trygve Reenskaug在1978年提出[1],是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件架构。MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,而且使程序某一部分的重复利用成为可能。除此以外,此模式经过对复杂度的简化,使程序结构更加直观。软件系统经过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。专业人员能够经过自身的专长分组:算法
MVVM是Model-View-ViewModel的简写,最先是由微软公司提出并运用,是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构架构。MVVM有助于将图形用户界面的开发与业务逻辑或后端逻辑(数据模型)的开发分离开来,这是经过置标语言或GUI代码实现的。MVVM的视图模型是一个值转换器,这意味着视图模型负责从模型中暴露(转换)数据对象,以便轻松管理和呈现对象。在这方面,视图模型比视图作得更多,而且处理大部分视图的显示逻辑。视图模型能够实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。
在MVC中很容易就会把一些业务逻辑,网络请求,数据IO都放在controller中 数据库
注意,这里不是说MVC的控制器必定很臃肿,而是「容易变得臃肿」 后端
在咱们新建一个工程的时候,苹果会自动帮咱们生成一个ViewController,而在动手开始写代码的时候,每每控制不住就直接将逻辑写在Controller中。网络
MVVM架构会要求咱们把任何与非View的逻辑玻璃出来,Controller中除了绑定viewModel以外的代码只容许出现对View的操做。由于Controller对咱们来讲也只是一个View。架构
就像上面说的,业务逻辑都会抽离出来放在viewModel中 ,这样能够在任何地方重用这一堆业务 框架
除此以外,咱们的代码将会更加易于测试,避免出如今MVC中可能出现的那种超长的方法、严重依赖全局状态致使难以测试的问题。数据库设计
在MVVM中,View只须要与ViewMode交互,不会收到其余的影响,因此不但vm、m 能够重用,view同样能够重复使用,修改的时候也更加方便。mvvm
因为在MVVM里面View和ViewModel是松耦合的,在测试出问题的时候就要排查各个地方的问题,布局
有多是vm中的也有多是view中的。因为vm会传递数据,一个bug会很容易的传递到其余地方,引起更大的问题。
而且其中一个地方出现问题的话,这个BUG就极有可能随着传递到其余的逻辑中,从而致使更严重的问题发生。
额外的viewModel使用也并非无代价的,有可能因为各类缘由致使管理起来稍微复杂。而额外的,若是由于强引用或其余缘由致使的循环引用等内存不能正确释放的状况下,有可能会内存疯涨,因此须要确保你的使用方式是无反作用的。
上面说了这些只是一个大体的介绍,咱们仍是来看看应该怎样使用吧。
你可使用delegate的方式或者block的方式对view和viewModel进行桥接,在这里咱们选择使用delegate,我认为这样看着比较直观,在代码中也更加明确。
首先建立一个工程,选择singleViewApplication,咱们就以最多见的的登录功能做为示例
首先要起个名字,就叫LoginViewModelDelegateProtocol 吧
@protocol LoginViewModelDelegateProtocol <NSObject> @end
好,让咱们想想view会发送一些什么数据给VM ,VM都须要什么数据。
对于简单登录的VM来讲,咱们须要通知view的数据和方法
那么咱们能够在protocol中添加方法了
@protocol LoginViewModelDelegate <NSObject> - (void)loginSuccess; - (void)showTips:(NSString *)tip; - (void)buttonEnable:(BOOL )enable; @end
同理,咱们只要确认vm和v须要交换的数据就行了。
@protocol LoginViewModelInterface <NSObject> - (void)inputUserName:(NSString *)uname; - (void)inputPwd:(NSString *)pwd; - (void)didTapLoginBUtton; @end
下面咱们新建一个viewModel叫作LoginViewModel
LoginViewModel.h
#import <Foundation/Foundation.h> #import "LoginViewModelDelegate.h" #import "LoginViewModelInterface.h" NS_ASSUME_NONNULL_BEGIN @interface LoginViewModel : NSObject <LoginViewModelInterface> @property (nonatomic, weak) id<LoginViewModelDelegate> delegate; @end NS_ASSUME_NONNULL_END
LoginViewModel.m
#import "LoginViewModelDelegateProtocol.h" @interface LoginViewModel () @property (assign, nonatomic) BOOL unameValid; @property (assign, nonatomic) BOOL pwdValid; @end @implementation LoginViewModel - (void)inputUserName:(NSString *)uname { self.unameValid = uname.length>0; [self judgeAllValid]; } - (void)inputPwd:(NSString *)pwd { self.pwdValid = pwd.length>0; [self judgeAllValid]; } - (void)didTapLoginBUtton { // 一些请求,这里忽略网络请求,直接模拟结果 [self.delegate loginSuccess]; } - (void)judgeAllValid { BOOL v = [self isAllValid]; [self.delegate buttonEnable:v]; } - (BOOL)isAllValid { return self.unameValid && self.pwdValid; } @end
而后在controller初始化,而且实现所有的方法就能够了。
viewController.m
#import "ViewController.h" #import "LoginViewModelDelegate.h" #import "LoginViewModel.h" @interface ViewController () <LoginViewModelDelegate, UITextFieldDelegate> @property (nonatomic ,strong) LoginViewModel *vm; @property (weak, nonatomic) IBOutlet UITextField *unameTxf; @property (weak, nonatomic) IBOutlet UITextField *pwdTxf; @property (weak, nonatomic) IBOutlet UIButton *loginBtn; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.vm.delegate = self; } - (IBAction)toggleLogin:(id)sender { [self.vm didTapLoginBUtton]; } #pragma mark - UITextFieldDelegate - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { NSString *str = [textField.text stringByReplacingCharactersInRange:range withString:string]; switch (textField.tag) { case 0: [self.vm inputUserName:str]; break; case 1: [self.vm inputPwd:str]; break; } return true; } #pragma mark - VMDelegate - (void)loginSuccess { [self showTips:@"Login Success"]; } - (void)showTips:(NSString *)tip { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Tip" message:tip preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *action = [UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel handler:nil]; [alertVC addAction:action]; [self presentViewController:alertVC animated:true completion:nil]; } - (void)buttonEnable:(BOOL )enable { self.loginBtn.enabled = enable; } #pragma mark - Getter - (LoginViewModel *)vm { if (!_vm) { _vm = [LoginViewModel new]; } return _vm; } @end
这里所有的代码是我手写的,后面省略了一些UIKit相关的布局,想必以你的聪明才智应该已经能很轻松的将剩余的补全了吧。
这里有几点我认为应该注意的:
总而言之,我认为MVVM在咱们的代码总体分工和应用架构的过程当中应用仍是十分优雅的,
不过话说回来什么架构也罢,仍是要看什么场景的,脱离了场景说这些都是扯淡不是吗