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瘦身的还有很多,以后项目中会继续总结。。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容