iOS开发 简化view controller

翻译、修改自objc.io
原文链接:Lighter View Controller

导语

view controller通常是一个项目中最庞大的文件,因为它里面经常包含了不属于它的代码,同时这也使它成为代码中最难以重用的部分。所以为view controller瘦身,让其中的代码复用性更强,把相关代码放到正确的地方显得尤其重要。


将Data Source和其他协议分离

为view controller瘦身最有效的方法就是把UITableViewDataSource中的代码移动到相关的类中,具体的方法可以参阅《iOS应用开发 简明TableView》中的相关实现。

而更进一步,不只是TableView,这个方法可以扩展到其他的协议上,比如UICollectionViewDataSource。如果在开发中选择使用UICollectionView代替UITableView时,这个方法可以让你几乎不用修改viewController中的任何东西,甚至可以让Data Source同时支持两个协议,给予了极大的便利性。

将弱业务逻辑移到Model中

首先是代码,以下的代码是帮助用户查找优先事项的列表:

-(void)loadPriorities
{
    NSDate *now = [NSDate date];
    NSString *formatString = @"startDate <= %@ AND endDate >= %@";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:formatString, now, now];
    NSSet *priorities = [self.user.priorities filteredSetUsingPredicate:predicate];
    self.priorities = [priorities allObjects];
}

然而,如果把这些代码移动到User类中会让它变得更加明晰,这时ViewController.m中会是:

-(void)loadPriorities
{
    self.priorities = [self.user currentPriorities];
}

User + Extensions.m中则是:

-(NSArray *)currentPriorities
{
    NSDate *now = [NSDate date];
    NSString *formatString = @"startDate <= %@ AND endDate >= %@";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:formatString, now, now];
    return [[self.priorities filteredSetUsingPredicate:predicate] allObjects];
}

将这些代码移动的根本原因是因为ViewController.m是大部分业务逻辑的载体,本身代码的复杂度已经很高,所以这类跟业务关联不大的代码比如日期转换、图像裁剪、设定过滤器等的操作可以分离到各自的类中完成,一方面为viewController减负,另一方面也能增进代码的复用。

关于这个标题的翻译我斟酌了比较久的时间,因为在原文中是“Move Domain Logic into the Model”,意为“把领域逻辑移到Model中”。对于“领域逻辑”一词我进行过考究,大致意思为“稳定的、不会改变的逻辑关系”,同时在原文中也是使用了NSPredicate作为例子引用,而我认为其例子中的代码也是与业务相关的,只不过关联性不大,而且不会轻易改动,所以使用了“弱业务逻辑”一词代替了“领域逻辑”一词。

把数据处理的逻辑移到服务层

一些代码可能没办法很有效的移动到model中,然而这些代码却和model中的代码有清晰的关联,对于这种问题,可以使用Store。比如在下面的代码中,viewController需要完成从一个文件中获取一些数据,并对其进行操作:

-(void)readArchive 
{
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSURL *archiveURL = [bundle URLForResource:@"photodata" withExtension:@"bin"];
    NSDate *data = [NSData dataWithContentsOfURL:archiveURL options:0 error:NULL];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    _users = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"users"];
    _photos = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"photos"];
    [unarchiver finishDecoding];
}

事实上,view controller不需要清楚怎么实现这些东西,而应该将这些处理交给一个store object来完成。

通过对代码进行分离,能够增进代码复用、对代码进行单元测试、保持view controller整洁等。同时能够让view controller更多关注于业务本身的内容,把数据的读取 、缓存、新建等操作交给服务层来处理。

把网络服务的逻辑移到Model层

这与上面提到的十分相似:不要把网络服务的逻辑放到view controller中,而应该把它们存放到不同的类中。

对于view controller,应该只是使用一个completion block来调用这些方法,而把网络请求、错误处理、缓存处理交给这些类来完成

把处理view的代码移到view层

无论是使用Storyboard还是纯代码编写view,创建复杂的view的任务不应该交给view controller。

比如在需要创建一个日期选择器时,更好的方法是把这些代码放到DatePickerView中,而不是放到ViewController中,和之前一样,是为了复用和简便。至于如何使用storyboard对view进行设置这里不再赘述。

Communication(通讯)

view controller中最常发生的就是通讯,包括了和view层的通讯、和model层的通讯、和其他view controller的通讯等。尽管这是一个view controller必须做的事情,这里依然有办法能够对代码进行缩减。

对于view controller和view、view controller和model之间的通讯已经存在大量的优秀经验,比如使用KVO键值模式传值,或者使用Core Data中的NSFetchedResultsController等。然而,对于view controller之间的通讯的相关方法却比较少。

比如在我现在做的项目中,一个view controller需要根据使用者身份的不同(家长/老师)来对view controller的 state 进行不同的设置,而这个 view controller 又在不同的 state 下与不同的 view controller 通讯,传递不同的值。在这种情况下 view controller 中的代码会变得相当臃肿和混乱,所以正确的做法是把这些不同的 state 放到不同的 object 里面,再把它们传给 view controller ,让它们根据这个 state 来进行设置和修改,使我们不再需要被累赘的委托方法搞得十分混乱。

而在另一种情况下,各个 view controller 之间的跳转逻辑十分复杂,存在着严重的横向依赖,在这种情况下就不适宜使用普通的页面跳转模式,而应该使用“中介者模式”,创建一个coordinator controller,让它来管理页面跳转的逻辑。


结论

事实上现在有各种各样的方法为 view controller 减负,它们无一例外都是向着一个目标前进:写出可维护的代码,只有把这些方法灵活运用,熟记于心,才能真正避免弄出笨重而又难以维护的 view controller。


想了解更多内容可以查看我的主页

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

推荐阅读更多精彩内容