当前位置: 首页 > 科技观察

总结整理下快速开发MVVM框架

时间:2023-03-16 10:25:20 科技观察

做iOS开发有一段时间了。最近在业余时间学习和研究MVVM。每个人对架构和设计模式的理解都不一样。在此记录下我对MVVM的一些小感悟,仅供参考,欢迎批评指正。概述MVVM的出现主要是为了解决Controller在开发过程中越来越大,变得难以维护的问题。因此,MVVM将数据处理的任务从Controller中解放出来,让Controller只需要专注于数据的部署。ViewModel负责数据处理,并允许View通过通知机制响应ViewModel的变化。MVVM是基于胖模型的架构思想建立的,然后从胖模型中分离出两部分:Model和ViewModel。ViewModel本质上是Model层(因为是胖Model的一部分),所以View不适合直接持有ViewModel,因为ViewModel可能不仅仅服务于具体的View,更松散的绑定关系可以降低耦合度在ViewModel和View之间。还有一个容易被忽视的问题。大多数国内外资料都是这样解释MVVM的:ViewViewModelModel造成了MVVM不需要Controller的错觉。现在看来MVVM不需要Controller了。响起。其实MVVM必须要有Controller的参与,虽然MVVM在一定程度上弱化了Controller的存在,减轻了Controller的负担和重量(这也是MVVM的主要目的)。但是,这并不意味着MVVM中不需要Controller。MMVC和MVVM的关系应该是这样的:ViewCViewModelModel,所以用了MVVM之后,说不用Controller是不正确的。严格来说,MVVM其实就是MVCVM。从这里可以看出,Controller在View和ViewModel之间做的一件主要事情就是绑定View和ViewModel。从逻辑上讲,Controller知道应该显示哪个View,Controller也知道应该使用哪个ViewModel。但是View和ViewModel互不认识,所以Controller负责控制它们的绑定关系,所以叫Controller/Controller就是这个道理。前面说了那么多,其实归结起来就是一句话:在MVC的基础上,就是MVVM把C拆分成一个负责数据处理的ViewModel。然后,为了让View和ViewModel之间有一个比较松散的绑定关系,我们使用ReactiveCocoa,KVO,Notification,block,delegate,target-action都可以用来进行数据通信,从而实现绑定,但是都不是as与ReactiveCocoa提供的RACSignal一样优雅。如果不使用ReactiveCocoa,绑定关系可能不会那么松和那么好,但不影响它还是MVVM。MVVM(View-ViewManger-C-ViewModel-Model)View-用于呈现用户界面ViewManger-用于处理View的常规事件,负责管理ViewController-负责ViewManger和ViewModel的绑定,负责生命周期控制器本身的周期。ViewModel-存储各种业务逻辑和网络请求Model-用于呈现数据这样设计的目的是保持View和Model的高度纯净,提高扩展性和复用性。在日常开发中,ViewModel的存在是为了拆分Controller的业务逻辑,所以ViewModel需要提供一个公共服务接口,为Controller提供数据。ViewManger的作用相当于一个小管家,帮助Controller分别管理各个子View。ViewManger负责接管来自View的事件和接收来自Controller的模型数据,而View则执行它负责的视图数据绑定工作。Controller是leader,负责绑定ViewModel和ViewManger进行数据转发。将合适的数据模型分发给合适的视图管理器。??在日常开发中,往往一个视图页面由一个controller来管理,一个页面上有N个小的子页面,这就需要我们对这些视图进行适当的分层,拆分视图,封装这些视图,将复杂的View抽象成独立的类而不公开具体的实现细节。这样做的好处是简化了应用层的层次复杂度,方便管理,因此视图结构变得非常清晰。子视图的具体内部事件可以通过代理方式或者block的方式交由ViewManger来处理,因为视图是可以复用的,而且其中的事件响应代码往往会根据不同的业务而有所不同。所以可能会出现以下两种情况:View很纯粹,需要复用,业务逻辑发生变化需要切换ViewManger。ViewManger也比较纯粹。如果业务逻辑不变,但View需要做较大改动,切换View即可,保证View中的协议或block一致<***是通过协议提前指定的>。这样就实现了相互封装,两者只通过协议或块进行通信,降低了代码的耦合度。尽量使用protocol和category来制定对象间的通信规范,减少代码的侵入性。这样的架构设计就像一条生产线,ViewModel负责收集和处理数据,Controller负责组装和转发数据,ViewManger负责接收和转发指派的数据,从而负责View的显示和管理视图的事件。这样无论哪个环节都是可替换的,也提高了复用性。架构解说上图作为解说demo。虽然很简单,但是也可以很好的解释。最重要的是理解这个想法。首先我们把这个页面拆分一下,第一个是controller。我们暂时将其命名为MyController。它上面有两个直接的子视图,按钮MyBtn和具有更复杂页面的子视图MyView。MyView有MyViewBtn1和MyViewBtn2以及一个MyViewLabel视图。具体结构如下:??接口分析完了,现在可以做代码的结构了。首先,你需要创建一个ViewModel,让它可以不断的产生数据,提供数据给MyController;然后创建一个ViewManger来管理MyView,当然Model模型数据是必不可少的。这些任务完成后,代码结构就变成了:controller中的代码结构如下:当用户点击MyBtn按钮触发一个action时,controller会将ViewMode中加载的数据模型转发赋值给in中的sui_model属性用于接待的ViewManger。-(IBAction)clickBtnAction:(UIButton*)sender{self.thirdViewManger.sui_model=[self.viewModelgetRandomData];}其中MyViewModel中加载代码如下。上面说了,它的工作就是分解一些之前controller做的事情。-(void)vm_getDataSuccessHandler:(void(^)())successHandler{//博客中省略,详见demo}-(instancetype)getRandomData{if(self.dataArrayList.count>0){u_int32_tindex=arc4random_uniform((u_int32_t)self.dataArrayList.count);returnsself.dataArrayList[index];}returnil;}MyViewManger中的代码如下,实现了MVVMViewMangerProtocol协议的三个方法://该方法用于接收和处理一些来自托管视图事件的消息。-(void)handleViewMangerWithSubView:(UIView*)subView//该方法通过view的父view来布局当前View-(void)handleViewMangerWithSuperView:(UIView*)superView//根据传入的view和info信息实现具体方法分别-(void)handleViewMangerActionWithView:(UIView*)viewinfo:(NSString*)info-(void)handleViewMangerWithSubView:(UIView*)subView{__weaktypeof(self.thirdView)weakThirdView=self.thirdView;__weaktypeof(self)weakSelf=self;//btnClickBlockweakThirdView.btnClickBlock=^(){[weakSelfhandleViewMangerActionWithView:weakThirdViewinfo:@"click"];};//btnJumpBlockweakThirdView.btnJumpBlock=^(){[weakSelfhandleViewMangerActionWithView:weak;Third;}"Viewjumfo"}-(void)handleViewMangerWithSuperView:(UIView*)superView{self.thirdView.frame=CGRectMake(0,66,[UIScreenmainScreen].bounds.size.width,200);[superViewaddSubview:self.thirdView];}-(无效)handleViewMangerActionWithView:(UIView*)viewinfo:(NSString*)info{if([infoisEqualToString:@"click"]){[viewconfigureViewWithCustomObj:self.sui_model];}else{FirstVC*firstVC=[UIViewControllersvv_viewControllerWithStoryBoardName:@"Main"identifier:@"FirstVCID"];[view.sui_currentVC.navigationControllerpushViewController:firstVCanimated:YES];}}MyView中的代码如下,主要负责管理自己的内部控制视图,并根据业务逻辑的需要定义一些基本事件,交给ViewManger实现:btnClickBlock();}}-(IBAction)jumpOtherVC:(UIButton*)sender{if(self.btnJumpBlock){self.btnJumpBlock();}}//根据传入的模型配置要显示的内容-(void)configureViewWithCustomObj:(id)obj{if(!obj)return;ThirdModel*thirdModel=(ThirdModel*)obj;self.testLabel.text=thirdModel.title;}这样就把各个部分区分开来了,是不是感觉代码结构很清晰?当然可以根据个人习惯进行修改,代码实现因人而异,但思路确实是可以互通的。将合适的业务逻辑交给最合适的对象去处理和实现。你只需要遵守这样一个基本原则即可。至于是否采用更轻量级的ViewController方式,即通过将各个协议的实现移出ViewController来实现ViewController的瘦身,众说纷纭。以UITableView为例,我的做法是:如果只是在页面上做简单展示,没有设计负责任的业务逻辑,UITableViewDelegate和UITableViewDataSource会放在一个Handler时钟里处理,tableHander抽象出来,MVVMTableDataDelegate会负责管理;当然,在实际开发中,每个tableView页面都非常复杂,需要处理的逻辑也很多。这时候只能考虑将协议返回给Controller,因为View层和ViewController层本身就持有Dependence关系和被持有,所以任何作为ViewController实例实现协议回调的类实际上都是跨层调用的。因此,随着时间和业务逻辑越来越复杂,就注定要以使用额外的接口为代价,换句话说,ViewController的内聚性越来越差。总之,具体情况具体分析,针对不同的问题采用最??合适的方式来处理。总有解决问题的方法。本文相关demo见github(https://github.com/lovemo/MVVMFramework)。实现的功能并不复杂,仅供参考。欢迎补充。