iOS 開(kāi)發(fā)中為controller 瘦身

在以往的開(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)行處理


屏幕快照01.png

在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è)面


屏幕快照03.png

當(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é)。。。。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容