原文发表在 近期重构工做的一点收获git
之前作我的项目的时候,简历上写太重构了三次,后来在扇贝面试的时候,面试官问三次分别重构了什么,仔细想一想那时候的重构并不算重构,第一次是 UI 改版,可是项目结构没什么大的变化,第二次是总体迁移到了 CocoaPods,此次勉强能算重构,第三次仅仅是变量名方法名空行这些地方的风格统一而已。面试
在如今工做的地方,接手这些项目以后,主要工做作的是重构,而重构工做,原本想写成一行,结果发现挺多,我列个列表吧:json
关于删除代码,在某个项目里,Pods 文件夹那些第三方库的代码删了 9 万多行(那个目录没有被 git ignore 掉),项目里面删除了大约 4 万行,其中大量代码是该项目以前的项目里面留下来的东西,只不过没人清理。在删了 4 万行以后,程序仍然能完整的跑。后端
接下来是作了部分重构,把一些第三方库删掉,本身造轮子,在这个过程当中,累计删除了 1.2 万行代码,增长了 1100 行左右。安全
整个重构工做下来,编译速度从 2-3 分钟减少到了 40 多秒,warning 从 70 多减小到了 0,第三方库的数量从 51 个减小到了 13 个,安装包从 22.1M 减少到了 3.7M,功能反而比以前还要多。网络
内存泄露方面,由于没人在乎这件事,有一个功能使用一次,就会增长好几百 kb 内存,那部分代码是用 C 写的,因此及时释放内存,而且优化下调用方式,内存泄露的问题就完美解决。app
循环引用方面,是由于有人把 Xcode 的 warning 关了,后来打开的时候,发现了四个循环引用 + 几十个 warning,而且测试过程当中发现那个页面不断打开退出,程序会 crash。函数
笼统的就这么多,我再来分享几个具体的点。工具
单例用着确实爽,可是程序退出以前是不会被回收的,若是是整个生命周期基本用不到的模块作成单例,那么只会浪费内存而已。性能
具体是什么意思呢,用网络层举例子,封装 AFN 是一层,API 的后缀字符串放一层,构造请求放一层,OAuth 受权放一层,发普通请求又是一层。增长一个 API,至少要修改 6 个文件。写着也很痛苦,看着也很痛苦啊。
网络层就只设计一层,封装 AFN,发请求的函数也在里面,API 地址直接用字符串写进去,搞那么多层没实际意义,在这么小的一个项目里面。
除此以外,关于项目文件结构,一两个文件的建议不要新建文件夹放进去,这个主要是我的习惯,其实无大碍。
- (void)requestAtPathForRouteNamed:(NSString *)routeName object:(id)object parameters:(NSDictionary *)parameters
复制代码
和
- (void)requestWithMethod:(XXHTTPMethod)method path:(NSString *)path params:(id)params paramsType:(XXParamType)paramsType
复制代码
以前这样设计的目的是 param 放 form data 类型数据,object 放 json 格式,显然不合理,同一个 API 不该该容许同时存在 form data 和 json,若是采用第一种,新来的同事可能会认为这两个均可以填数据,这是不符合咱们指望的。
甚至再极端一点,某天咱们须要传文件过去,是否是还得再扩充字段。
若是采用第二种,param 是 id 类型,若是是 json,type 传入 json 枚举类型,若是是二进制,type 传入二进制枚举类型,只留一个字段暴露给开发者更合理。
咱们的项目中有一条渐变颜色的线要处处用到,这条线咱们放在了 UIImage+XXUtil.h 里面,以前的设计是这样的:
+ (instancetype)xx_navigationBarShadowImage
复制代码
在 .m 的实现中,还把 UIColor+XXTheme 耦合进去了,而且这个方法已经脱离了类名 Util 的实质,他已经不是一个通用的工具了,重构以后的命名是这样的:
+ (instancetype)xx_gradientImageWithStartColor:(UIColor *)aColor endColor:(UIColor *)bColor andWidth:(CGFloat)width
复制代码
这样就很符合 Util 这个 category 名字。
继承确实很好用,带来的后果就是子类会把父类的方法挨个执行一遍,乍一看没什么,可是若是这个方法很消耗性能呢。
咱们这个项目就遇到了,app 常常卡死,用着用着,就 freeze 了,点哪里都没反应。由于全部页面都继承自基类的一个设计,刚好基类里面有一个比较耗时的操做,每一个页面都会执行至少三次,就致使了页面假死。
重构后的作法是设计成一个 category,只是给 UIViewController 添加了几个方法,按需调用,不须要在每一个页面都调用,因而解决了这个诡异的 bug。
若是有一个功能,迫于各类缘由,不得不采用第三方库,至少也要选一个 GitHub 上 star 比较多的吧,其次是看看 issue 列表有没有什么很严重的 bug 没修好,以及兼容性问题,多养成好习惯,慢慢就能筛选出来最合适的库了。
咱们的项目以前有用到 YYText 这个库,就为了一段文字里面加一张图片,活动当天 iOS9 设备出现好几百次 crash,实际上这段代码用 NSAttributedString attributedStringWithAttachment
写一下,七行就够了,七行替代掉一个不稳定的第三方库,仍是很划算的。
不知道由于什么缘由,多是更旧的项目里面用了 PSCollectionView,能跑就没去重构,这类库也是属于彻底不必的,系统自带的足够好用,而且更安全。
数据的处理,好比字符串进行 UTF8 编码,时间戳转成 YYYY-MM-DD 字符串,这些都放在 Model 层来处理,各司其职,Model 层就是作数据处理的。
后端把各类 ID 用 long 型来记录,是由于他们要作索引,为了索引速度。而客户端彻底没这个需求,直接用 string 就好,还不用担忧长度不够的溢出,作展现的时候还不用转类型。
一样的,金额按理说应该用双精度浮点型,由于 float 的精度不够,结合个人开发经验看,金额不多要客户端作加减,直接用 string 便可,须要计算的时候再转换,只转换一次,避免丢失精度。
苹果的 UIKit 就是最好的例子,写什么组件不知道名字怎么起的时候,就想一想苹果有没有相似的组件,去找找灵感。
OC 的方法名自己就很长很清晰了,只是给方法名中间加几个空格,而后做为注释,跟没写同样吧。
Git 的做用就是随时回溯之前版本,代码都是能找到的,把代码注释掉,再写一行相似的,除了增长阅读成本,容易引发歧义,应该没什么用了。一个文件一共一两百行,打开以后发现七八十行代码被注释了,这种感受至关操蛋,影响阅读。
重构祖传代码真的会有一种活久见的感受,上面提到的那些是印象比较深入的,还有一些小问题已经悄悄解决了,但愿我写的代码不会让后面的同窗也这样认为吧。