iOS 开发中为controller 瘦身

在以往的开发中,使用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进行处理


屏幕快照01.png

在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

如图的结构页面


屏幕快照03.png

当前控制器可以再细分好几个模块的情况下,千万不要几个页面的逻辑操作写在一个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瘦身的还有很多,以后项目中会继续总结。。。。

你可能感兴趣的:(iOS 开发中为controller 瘦身)