每个初入项目组的新人都有过这样的经历,刚开始加入的时候,内心虽然踌躇满志,但面对陌生的业务逻辑和代码架构却一筹莫展,只能老老实实在前人的代码基础上添砖加瓦。开发一段时间后,虽然已经把代码逻辑理得差不多了,但是一些头疼的模块(dead code)也不敢轻易去动,美名其曰稳定高于一切,回归测试麻烦,直到逻辑越来越复杂,App 启动时间越来越长,开发编译时间从个位数变成两位数,后来者更是基本处于懵逼状态,勉强靠 git blame/log/comparisonFind Call Hierarchy 度日,不明白的地方一问:哦,这段代码没用了,你把它删掉吧。好吧,我删🙄。不删倒好,一删则拔出萝卜带出泥,一连串300个 error 算轻的。

谁都不愿意一个项目长时间背负很严重的历史包袱,首当其冲就是需要将一些无用代码(dead code)删掉,但是甩历史包袱的同时你必须得保证两点:一是过程顺利零error,否则你会抓狂;二是删掉后整个项目功能性无影响。差不多花了一天时间调研加实操,我暂时在我们云课堂企业版主工程中安全地删掉了近200个无用文件,接下来讲一讲我在此过程中的一些心得总结。

调研

删掉无用代码是我们的目标,在 iOS 开发中,最容易想到的就是如果一个类没有被 import,而且没有被通过反射动态获取,那么可以认为这个类文件已经是 dead code 了。在和同事骥文饭后散步之余,我聊起这个问题,他告诉我说目前很多代码虽然没有用,但还是被 import 了。所以解决思路大致也出来了:

  1. 先遍历整个项目,将每个文件中的无用 import 去掉
  2. 再找出未被 import 的文件,将其删掉

找出无用的 import 语句

找出无用的 import 语句实际上是一个工业界的问题,虽然自己写个脚本也可以搞定,但解决思路一般都是本着DRY(Don’t repeat yourself)的原则,看看业界有没有现成的轮子可用。打开 Google,输入

1
xcode detect unused import statement

还真找到了一个 dblock/fui,但实际体验不太好,弃之。

看了一圈,都没找到比较满意的,突然想起来之前写 Java 的时候 IDEA 和 Eclipse 都可用检测出无用的 import,JB(jetbrains)家的 IDE 都是一副德行,那么 AppCode(用于写 iOS 的 IDE )是不是也支持呢?

马上安装试试:

1
brew cask install appcode

还真是!先选中项目,然后直接选择 Code -> Optimize Imports,分分钟移除了327个文件的无用 import(git 有提交记录的)。然后在 Xcode 中重新编译,成功,说明 AppCode 还是挺智能挺靠谱的。

分析了一下这个原理,主要还是看 import 的类有没有被初始化,大概会检查几个方法:

  1. alloc
  2. allocWithZone:
  3. copy
  4. copyWithZone:
  5. new

所以在我看来还是不要局限于自己领域的一亩三分地,多看看其他领域的一些成熟的解决方案,博采众长,视野也会更宽阔。

找出未被 import 的文件

找出未被 import 的文件原理就更简单了,通过 project.pbxproj 文件可以找出工程中所有文件和被 import 的文件,两者相减就能得到结果了。

这个问题我也找到了一个现成的轮子,CatchZeng/CATClearProjectTool,但年久失修,有个重大 bug,不知道作者是不是觉得收割了一波 star 或是在简书发表完文章就万事大吉了。好吧,我 fork 了一份,修复了他的 bug,并且加了个指定过滤前缀的功能,代码放在 zhihuilong/CATClearProjectTool 上面。找到了未被 import 的文件,删起历史包袱来效率就高多了,而且基本上不担心功能稳定性的问题。只要保证通过反射获得的类不被删掉就行(如果有代码埋点动态下发一定要和同事事先确认),具体也就是全局搜索两个C方法即可:

  1. NSClassFromString()
  2. objc_getClass()

还可以提升的点

原本我以为文章写到这算是结束了,发给同事看了后说其实还可以就无用的资源文件,无用的方法、常量和宏清理再多谈一些。关于图片资源的清理,网上文章一大把,就不细说了。方法是否有用我觉得要在程序运行时才能判断,常量和宏有时间也可以写个脚本来清理一下。

如果你的项目历时时间长,历史包袱重,无用代码多,不妨按照我的方法安全地将其甩掉,轻装上路,既是为自己好,也尽量别给新人后来者埋太多坑。


阅读