在以往的开发中,使用MVC架构模式的时候还是居多的,MVC方便的是职责比较明确,View负责界面的展示,Model存储数据模型,controller负责业务逻辑,原先使用的时候,Model 使用的是瘦Model的形式,只存储数据模型,不处理业务逻辑,这就导致一个问题,就是controller的职责越来越重,需要处理的东西越来越多,包括页面的构建、网络请求的处理、页面的跳转,代理方法、响应方法等。这样的结果是导致controller越来越厚重,代码堆积越来越多,少则几百行代码,多则二三千行代码,这也会使代码的后期维护变得异常艰难,特别是接手别人的代码,修改bug定位问题会花费更多的时间,所以我们尽量要从controller抽离一部分的代码逻辑,为controller瘦身。
一、将View的代码移到View层
这一方面相信大多数人做的比较好,一个页面肯定有许多视图, 以首页为例子 包括 banner视图、点击跳转视图、列表视图、推荐视图、尾部视图、对于每一个模块的视图应该进行子类化封装,
#import "HomebannerView.h"
#import "HomeOperationView.h"
#import "HomeMainListView.h"
#import "HomeSecondKillView.h"
#import "HomeTrailsView.h"
然后在控制器中进行add
[self.view addSubview:self.containerView];
[self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.bottom.with.offset(0);
}];
[self.containerView addSubview:self.bannerView];
[self.bannerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.offset (0);
make.height.mas_equalTo (143 * KHEIGHT_IPHONE6_SCALE);
}];
//...后续View的构建添加
二、将网络请求移动到Model层
新的项目之后 ,除了Model、View、Controller之外,我会单独新建一个ViewModel类,用于处理部分逻辑,在.h文件中将逻辑的方法暴露给controller调用,逻辑处理完将 处理结果回调给controller进行处理
在ViewModel中进行网络数据的获取以及解析或者后续的逻辑操作
///IntelGeneDetailViewModel.h 文件
//组合产品是否可投资信息
@property (nonatomic, strong) NSError *investDataError;
@property (nonatomic, strong) InGenDetaiInvestInfoModel *investInfoModel;
@property (nonatomic, assign) BOOL isInvestInfoCompleted;
//请求曲线数据
- (void)requestForTrendLineDataWithPortfolioId:(NSString *)portfolioId portfolioType:(NSString *)portfolioType duration:(NSString *)duration;
// 获得组合产品是否可投资信息
- (void)requestForInvestEnableInfoWithPortfolioId:(NSString *)portfolioId portfolioType:(NSString *)portfolioType content_pk:(NSString *)content_pk;
// IntelGeneDetailViewModel.m 文件
/**
请求组合详情页数据
*/
- (void)requestForGenDetailDataWithPortfolioId:(NSString *)portfolioId portfolioType:(NSString *)portfolioType {
NSMutableDictionary *dataDic = [NSMutableDictionary dictionary];
[dataDic setObject:portfolioId forKey:@"portfolio_id"];
[dataDic setObject:portfolioType forKey:@"portfolio_type"];
[dataDic addEntriesFromDictionary:self.baseParams];
[dataDic addEntriesFromDictionary:self.userParams];
[[ATTNetwork shareInstance] getWithURL:APIManager.intelGetPortfolioDetailInfo params:dataDic isLoading:YES success:^(id response) {
[self handleIntelGenDetailResponse:response];
} failure:^(NSError *error) {
self.intelGenDetailError = error;
}];
}
- (void)handleIntelGenDetailResponse:(id)response {
response = [Util dictionaryWithJSON:response];
DebugLog(@"组合详情页数据=%@",response);
BOOL isSuccess = [response[@"is_success"] boolValue];
if (!isSuccess) {
NSString *errorMessage = response[@"error_msg"];
[[UIViewController currentViewController] showToast:errorMessage];
return;
}
NSDictionary *dataDic = response[@"data"];
IntelGenDetailModel *intelGenDetailModel = [IntelGenDetailModel mj_objectWithKeyValues:dataDic];
self.intelGenDetailModel = intelGenDetailModel;
}
处理完的数据结果可以通过 block 、或者代理等方式回传给controller做进一步的数据处理,笔者习惯使用RAC的方式 在controller 监听 viewModel中值的变化来进行值的回传。
//页面数据
[[RACObserve(self.mainViewModel, intelGenDetailError)ignore:nil]subscribeNext:^(NSError *error) {
@strongify(self);
DebugLog(@"错误代码=%ld, 错误描述%@", error.code , error.description);
[self handleNetworkError:error];
}];
[[RACObserve(self.mainViewModel, intelGenDetailModel)ignore:nil]subscribeNext:^(IntelGenDetailModel *model) {
@strongify(self);
self.detailModel = model;
[self handleDataAfterCompleted];
}];
其实在抽拟的viewModel层不单单可以进行网络请求,还可以进行部分逻辑的处理,每个模块的高度计算,模块的隐藏与显示,跳转的逻辑等等
三、结构复杂的页面建议使用child view Controller
如图的结构页面
当前控制器可以再细分好几个模块的情况下,千万不要几个页面的逻辑操作写在一个controller中,如果那样做,那简直是噩梦。随着每个页面中逻辑和页面结构的复杂性,controller中的代码会越来越不可控。所以建议一旦一个页面下面有明显的可以细分的页面,最好使用child viewController,简单的逻辑如下
[self addChildViewController:currentVC];
[currentVC didMoveToParentViewController:self];
currentVC.view.frame = CGRectMake(SCREEN_WIDTH * _initiaIndex, 0, SCREEN_WIDTH, SCREEN_HEIGHT - 49 - 64);
[self.pageScrollView addSubview:currentVC.view];
总结
其实这些是在项目中使用的最基础的 Controller 瘦身的一些做法,其实整个项目中 用的最多的还是 第一条和第二条的总结,因为所有的控制器都会有这样的操作,有时候都会在Controller中一不小心就会写下几千行的代码,其实代码规范应该作为自己的代码习惯去遵守,写出可用性、可读性的代码是每个程序员应该做到的,这样保证自己项目的可维护性以及后续的可扩展性,即便是别人接手了你的代码,至少不会骂人,😆😆。。。其实需要Controller瘦身的还有很多,以后项目中会继续总结。。。。