如何瘦身UIViewController

随着程序逻辑复杂度的增加,你是否也发现App中某些ViewController的行数急剧增加,达到了2000或3000行,甚至更多?这个时候,如果你想增加一点功能或者修改现有的逻辑,就变得很头疼了。如果遇到这样的问题,是时候停下来想想如何更好的整理代码,瘦身VC了。下面将解释如何结合MVC思想来帮助你的VC瘦身,提高重用性和可扩展性。

第一,发展中的普遍现象和不足

iOS中最常见的设计模式之一就是MVC,但在实际开发过程中,由于这样或那样的原因,我们把简单的ViewController变成了模型、控制器、视图的大集合,这必然导致VC的代码容量呈几何级增长。这种代码有几个问题:

1,不利于后续维护。

代码在公司的存活时间通常比你在公司的时间长很多。你是否在接手已有项目后,看完代码在脑子里说“一坨屎”?我想没有人希望接手你代码的人有一天读到你的代码,脑子里说着同样的话。作为一个有抱负的程序员,或者说为了不被以后的同事骂,我们必须对自己的代码负责,避免成为一个有几千行的源文件。你甚至不想读你写的东西,更不用说那些后来接手的人。

如果项目很急,当时没时间合理拆分重构代码,后面挖的坑必须花时间补上。你可能会说公司一直很忙,没时间给你重组。我只能说,要么你自己安排不了时间,要么这个公司只把你当代码搬运工。从长远发展来看,要么改变自己,要么炒了老板。

2.不利于支持UI的变化。

想象一下,如果有一天你的App的UI风格需要改变,大量的视图需要改变。在一个几千行的VC里删除修改是什么感觉?可能是因为在更改UI时,不小心更改了模型或控制器中的某些东西,导致程序无法正常运行。而且变化的范围不能控制在很小的范围内,测试回归的工作量也很大。

3、不利于重用

如果你的App一开始只支持iPhone版本,一切都会那么自然,程序也会运行的很好。突然有一天,老板告诉你,公司业务发展不错,为了拓展市场,需要退出iPad版。这时候,如果只是iPhone版本的放大版,那么你需要做的可能就是增加一些view的适配。然而,现实并不总是理想的。如果iPad版本需要重新设计,按键位置发生了变化(参考上面第二点),那么这个时候是否需要更改所有代码?这他妈的工作量太大了。

通常无论UI如何变化,iPhone版和iPad版的业务逻辑基本是一样的,那么如果一开始我们的代码层次清晰,控制器和模型层能完美复用吗?只需为不同版本更改一组视图。这样是不是方便多了?自己感受一下。

第二,如何解决这些问题

第一部分说了这么多终于说到点子上了。如何利用MVC模式更好的瘦身VC,提高复用性和可维护性?我画了下面这张图:

解释一下上图,一个完整的模块分为三个相对独立的部分,分别是模型、视图和控制器,对应我们App中从NSObject继承的数据中心,视图承载UI显示和事件响应,以及我们最常用的UIViewController。

其中,VC持观点和模型。View通过proxy或者Target-Action把用户的操作传递给VC,VC负责根据不同的用户行为做出不同的响应。如果需要加载或刷新数据,可以直接调用Model公开的接口。如果可以同步获取数据,可以直接用获取的数据刷新视图。如果需要通过网络请求等其他异步方式获取数据,VC会监听Model发送的数据更新(成功或失败)通知,收到通知时根据成功或失败刷新视图。可以看出,整个过程中视图和模型之间没有直接的交互,所有的操作都是通过VC来协调的。

经历MVC分层的好处:

1,VC代码量骤降,易维护。

可以看到,拆分后VC中只剩下事件响应操作,所有显示相关的东西都单独提取,所有网络请求和数据缓存都提取。VC中的代码会大大减少,我们项目中的实际结果是,之前VC中的代码行数是2600,拆分后的VC中的代码行数不到600。

2、提高了可重用性

拆分后,如果App需要对UI显示进行重大改动,那么你的改动基本都会停留在视图模块中。你可以选择在现有的基础上修改或者从头写一个。只要业务不变,模型和VC模块根本不需要修改。这个改动范围小,对开发和测试友好。

拆分之后,如果App需要支持iPad版本,那么你需要做的就是重写一个视图,放进去。模型和VC模块基本不需要修改。想想是不是还有点激动。

第三,总结

使用MVC模式可以帮助VC瘦身,提高复用性和可维护性,同时也会使原来的整个业务代码分散到不同的地方。在实际使用中,需要具体问题具体分析。如果一个VC里的东西很简单,完全可以放在一起,因为即使全部放在一起,也只有几百行代码。比如App本身常见的版权接口,只是加载了一个html,没必要搞得那么复杂。如果一个VC已经很复杂了,代码成千上万行,那么就需要拆分,以达到更好的复用和方便维护。

我写代码已经好几年了。我见过把所有东西都塞进一个源文件,也见过一个简单的东西被设计模式撕成碎片。不要仅仅为了使用设计模式而使用它们。把握好度很重要,子弹能解决的问题不要用大炮。

代码重构应该是一个持久的过程。在开发的过程中,对存在的不合理的地方不时进行重构,而不是等出现很多问题才想起重构。千里之行始于足下,千里之堤溃于蚁穴。