在城大的工作终于是结束了。用了「终于」两个字,是因为我再也不能承受这份工作了,虽然他们允许我远程工作,在工作中我的时间安排相当自由,做的内容也是比较自由,但是正因为太自由了,我发现我在里面投入了过多的时间与精力——描述太过「简单」的需求让我很难界定范围。而教授又希望他的 app 能够「充满不同游戏」,这个状态一直都在持续。

于是我找了一份朝九晚六周末休息、离家还算近的工作。一切都还挺好的,只是接手代码这件事让我有些难受。

先谈谈这个项目有什么问题。

Massive View Controller

每个页面基本上就是靠着一个 view controller 撑着,业务逻辑和 UI 逻辑混在一起,我看到过一个 2000+ 行的。

不够 MVC

这个项目里 view 绝对是「一等公民」。view 不仅能够控制页面跳转,有时还承担了一些业务逻辑。

将这些放到 view 去做就算了,还偷懒不给足够的上下文。之前就遇到了一个遗留 bug:一个弹窗弹到了错误的地方。原因是弹窗的逻辑写到了 view 里,而 view 竟然将弹窗作为 subview 加到了 [[[[UIApplication sharedApplication] delegate] window] rootViewController].view 上,显然在某一版之前这个逻辑(恰好)是正确的,某一版之后他们决定不把这个页面 push 到作为 rootViewcontroller 的 navigation controller 里面了,就出现了这个问题。
incorrect-position-of-popup

我猜这个 bug 已经存在很久了……这种写法也为接手的人带来了极大的困难,现在我需要每个 view 都点开看看才知道发生了什么。

View 的依赖太随便

better-way-to-pass-dependency

有些时候上下文也是给得太足。比如说有这么一个 cell,本来传进去三样东西就足够了,在这个项目中却为了写时方便传入了硕大无比的 object,再在 cell 的内部决定使用什么内容。
wrong-way-to-pass-dependency

对于没有接触过这个项目的人来说很难去理清究竟用了什么没用什么,我甚至会担心把这个 mutable 的东西传来传去,会不会发生了什么意料之外的写操作。另外就是要单独测试这个 cell 的话,就需要先去创建这样的一个巨大 object 了,很麻烦。

充满了 Magic Value

这次是中途接手了别人的 0.1.0 级别更新,我没有时间先大概了解这个项目和这个版本的需求,就要开始写代码了。到了最后一天的下午,设计师突然跟我说要把某个间距从 15 改成 20。这当然应该是很简单的事情,可却把我给难住了,因为这个间距所在的 UICollectionViewCell 的高度计算方法里是这种样子的:

1
2
3
4
5
6
7
8
CGFloat height = 0;
...
height += 15 + somethingsHeight + 20 + 5 + 10 + 15;
...
height += anotherHeight + 20 + 10 + 5;

if (showThis) { height += 5 + thisHeight + 20 + 10; }
return height;

更可怕的是,我尝试去改了一下数字,界面却没有发生变化。对不起,我只能想办法让它变宽一些,具体宽多少,我暂时还没有办法控制,等下一版吧。

在 iPhone X 的适配上也是使用了判断机型提供相应 magic value 的方式,我已经预感到下一次适配时的痛苦了。

太喜欢宏定义

并不是说宏定义不好,我也喜欢用宏定义来实现 Objc 下的 let 和 var,但是有些常量实际上用其他方式来表示会更好一些。比如说颜色,这个项目有一个主题色被定义成了常量

1
#define GLOBAL_TINTCOLOR UIColorFromRGB(0x12abcd)

我总是不记得这个宏叫什么,因为还存在很多其它名称各异的颜色常量,叫做 XXXX_COLORXXXX_COLOR_XXX 或者 ColorForXXXXX,甚至还有 COLOR_0xAAAAAA 这样的东西。要说它们命名能规范一些、定义能集中一些,问题倒是不大,但它们基本上是东一个西一个,名称格式也各式各样,除非记得名字,想找到它们还是很麻烦的,自动补全都帮不了你。

其实写成 UIColor 的 category 不就完了嘛。

太多 Warning

我第一次 build 这个项目的时候真是惊呆了,300 多个 warning,其中可能有一半是因为用了 iOS 9 或之前开始 deprecate 的 API。我掐指一算,9、10、11、12,这些难道不应该两年前就清掉吗,反倒还在继续用。我也没法现在就动手把它们全部改掉,只好先写个 wrapper 把它们都包住,关掉 deprecated functions 的 warning,日后再统一处理。

然后还有很多「Prototype xxx cells must have reuse identifiers」,我猜是上面的一百来个 warning 蒙蔽了他的双眼。还有一些「隐式类型转换可能会影响精度」啊、类型不匹配啊、或者输入了奇怪的符号但会被当作空格……能改的我都改了,还有一些莫名其妙的、不敢改的、来自第三方库的,现在还有 46 个 warning。

看到这么多 warning,你们就不心疼?心疼是小事,重要的 warning 被淹没了可是大事。

太喜欢第三方库,尤其是 UI 方面

用是可以的,但能不能不要挑那种没有 star、上传之后就没下文的库呢。这种库往往都没法开箱即用,拖下来也是一顿改。这读别人代码然后强行改成符合需求的时间,我觉得自己写早写完了,最终效果和过程体验都得好不少。

而对于一个接手项目的人来说,读这种充满补丁的代码更是噩梦。

干脆就复制粘贴吧

项目里充满了内部复制粘贴的代码。在很多情况下,复制粘贴其实还不错。但像以下这种类型的复制粘贴真的是非常糟糕了:

1
2
3
4
SomeViewController *vc = [SomeViewController new];
[vc initializeWithSomeProperties];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
[self presentViewController:nav];

天知道这要包在一个 UINavigationController 里面噢。

这种情况,把 SomeViewController 直接做成一个 UINavigationController 或者是用一个 navigation coordinator 来控制跳转,怕不是要强个一百倍。

这种代码的复制粘贴还有不少。