在以往的開(kāi)發(fā)中,使用MVC架構(gòu)模式的時(shí)候還是居多的,MVC方便的是職責(zé)比較明確,View負(fù)責(zé)界面的展示,Model存儲(chǔ)數(shù)據(jù)模型,controller負(fù)責(zé)業(yè)務(wù)邏輯,原先使用的時(shí)候,Model 使用的是瘦Model的形式,只存儲(chǔ)數(shù)據(jù)模型,不處理業(yè)務(wù)邏輯,這就導(dǎo)致一個(gè)問(wèn)題,就是controller的職責(zé)越來(lái)越重,需要處理的東西越來(lái)越多,包括頁(yè)面的構(gòu)建、網(wǎng)絡(luò)請(qǐng)求的處理、頁(yè)面的跳轉(zhuǎn),代理方法、響應(yīng)方法等。這樣的結(jié)果是導(dǎo)致controller越來(lái)越厚重,代碼堆積越來(lái)越多,少則幾百行代碼,多則二三千行代碼,這也會(huì)使代碼的后期維護(hù)變得異常艱難,特別是接手別人的代碼,修改bug定位問(wèn)題會(huì)花費(fèi)更多的時(shí)間,所以我們盡量要從controller抽離一部分的代碼邏輯,為controller瘦身。
一、將View的代碼移到View層
這一方面相信大多數(shù)人做的比較好,一個(gè)頁(yè)面肯定有許多視圖, 以首頁(yè)為例子 包括 banner視圖、點(diǎn)擊跳轉(zhuǎn)視圖、列表視圖、推薦視圖、尾部視圖、對(duì)于每一個(gè)模塊的視圖應(yīng)該進(jìn)行子類(lèi)化封裝,
#import "HomebannerView.h"
#import "HomeOperationView.h"
#import "HomeMainListView.h"
#import "HomeSecondKillView.h"
#import "HomeTrailsView.h"
然后在控制器中進(jìn)行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);
}];
//...后續(xù)View的構(gòu)建添加
二、將網(wǎng)絡(luò)請(qǐng)求移動(dòng)到Model層
新的項(xiàng)目之后 ,除了Model、View、Controller之外,我會(huì)單獨(dú)新建一個(gè)ViewModel類(lèi),用于處理部分邏輯,在.h文件中將邏輯的方法暴露給controller調(diào)用,邏輯處理完將 處理結(jié)果回調(diào)給controller進(jìn)行處理

在ViewModel中進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的獲取以及解析或者后續(xù)的邏輯操作
///IntelGeneDetailViewModel.h 文件
//組合產(chǎn)品是否可投資信息
@property (nonatomic, strong) NSError *investDataError;
@property (nonatomic, strong) InGenDetaiInvestInfoModel *investInfoModel;
@property (nonatomic, assign) BOOL isInvestInfoCompleted;
//請(qǐng)求曲線(xiàn)數(shù)據(jù)
- (void)requestForTrendLineDataWithPortfolioId:(NSString *)portfolioId portfolioType:(NSString *)portfolioType duration:(NSString *)duration;
// 獲得組合產(chǎn)品是否可投資信息
- (void)requestForInvestEnableInfoWithPortfolioId:(NSString *)portfolioId portfolioType:(NSString *)portfolioType content_pk:(NSString *)content_pk;
// IntelGeneDetailViewModel.m 文件
/**
請(qǐng)求組合詳情頁(yè)數(shù)據(jù)
*/
- (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(@"組合詳情頁(yè)數(shù)據(jù)=%@",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;
}
處理完的數(shù)據(jù)結(jié)果可以通過(guò) block 、或者代理等方式回傳給controller做進(jìn)一步的數(shù)據(jù)處理,筆者習(xí)慣使用RAC的方式 在controller 監(jiān)聽(tīng) viewModel中值的變化來(lái)進(jìn)行值的回傳。
//頁(yè)面數(shù)據(jù)
[[RACObserve(self.mainViewModel, intelGenDetailError)ignore:nil]subscribeNext:^(NSError *error) {
@strongify(self);
DebugLog(@"錯(cuò)誤代碼=%ld, 錯(cuò)誤描述%@", error.code , error.description);
[self handleNetworkError:error];
}];
[[RACObserve(self.mainViewModel, intelGenDetailModel)ignore:nil]subscribeNext:^(IntelGenDetailModel *model) {
@strongify(self);
self.detailModel = model;
[self handleDataAfterCompleted];
}];
其實(shí)在抽擬的viewModel層不單單可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求,還可以進(jìn)行部分邏輯的處理,每個(gè)模塊的高度計(jì)算,模塊的隱藏與顯示,跳轉(zhuǎn)的邏輯等等
三、結(jié)構(gòu)復(fù)雜的頁(yè)面建議使用child view Controller
如圖的結(jié)構(gòu)頁(yè)面

當(dāng)前控制器可以再細(xì)分好幾個(gè)模塊的情況下,千萬(wàn)不要幾個(gè)頁(yè)面的邏輯操作寫(xiě)在一個(gè)controller中,如果那樣做,那簡(jiǎn)直是噩夢(mèng)。隨著每個(gè)頁(yè)面中邏輯和頁(yè)面結(jié)構(gòu)的復(fù)雜性,controller中的代碼會(huì)越來(lái)越不可控。所以建議一旦一個(gè)頁(yè)面下面有明顯的可以細(xì)分的頁(yè)面,最好使用child viewController,簡(jiǎn)單的邏輯如下
[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];
總結(jié)
其實(shí)這些是在項(xiàng)目中使用的最基礎(chǔ)的 Controller 瘦身的一些做法,其實(shí)整個(gè)項(xiàng)目中 用的最多的還是 第一條和第二條的總結(jié),因?yàn)樗械目刂破鞫紩?huì)有這樣的操作,有時(shí)候都會(huì)在Controller中一不小心就會(huì)寫(xiě)下幾千行的代碼,其實(shí)代碼規(guī)范應(yīng)該作為自己的代碼習(xí)慣去遵守,寫(xiě)出可用性、可讀性的代碼是每個(gè)程序員應(yīng)該做到的,這樣保證自己項(xiàng)目的可維護(hù)性以及后續(xù)的可擴(kuò)展性,即便是別人接手了你的代碼,至少不會(huì)罵人,????。。。其實(shí)需要Controller瘦身的還有很多,以后項(xiàng)目中會(huì)繼續(xù)總結(jié)。。。。