一个普通的 5 年 iOS 开发者的经历和感想

谈谈背景

我是普通大学毕业,专业是电子信息工程,由于一些机缘巧合,我毕业后居然开始了 iOS 开发的生涯。再后来认识一些做移动端的朋友,有的是计算机专业出身,有的是学化学、机械的,我还见过新闻专业转行过来的神奇 boy. 可见,移动开发还真是什么人都可以做。😏

不过,现在事情已经起了变化。不久之前看到行业内比较有话语权的大佬发了如下的微博:

我不觉得这个有什么歧视的意思,何况大佬表示有能证明自己能力的 anything 都可以。

作为从事 iOS 开发好几年的开发者,我见识过 2013-2014 年移动端大热时的疯狂,见识过 2016 年一个 iOS 岗位对应千封简历的疯狂(培训班扰乱市场)。一个细分的行业从大热到遇冷,仅仅两三年的时间。而如今看到大佬的微博,感觉移动开发进入了一个新的阶段:移动端岗位需求正在快速减少,对从业者的要求越来越高。那种培训班三个月出来都能找份月薪过万的工作的事情早就是上古神话了。但是,这只不过意味着志向于从事移动端开发的普通开发者或者普通学校出身的同学需要更努力一点证明自己。

就像我十年前读到李开复的博客“二流学校的我该怎么办”里面有一段话:

毕竟复旦、交大、北大、清华是所有HR都知道的,你说你是某个地方某所小学校里出身的学生。HR可能不是很清楚那所学校的情况,所以对你有些先入为主的偏见,这很正常。毕竟站在公司的立场,他希望有secure,招人也是件很麻烦的事情。所以他要优先考虑你的背景和资质是否可以胜任或者是完全超越职位所需的。一旦你能拿出相关的证据(实实在在的)东西,那么即便你出身二流学校你仍然是很有希望的。如果你拿不出,那么坐在你旁边的名校生说我毕业于。。。就读于某个专业(这个是他的证据)。在双方都没有实在证据的前提下,那么“读书好”就成了一种支持性的证据,证明他比你优秀。

作为普通开发者,必须要有实实在在的东西证明自己的能力,才能在行业里保持竞争力。保持竞争力,不仅要有个人奋斗,也要注意行业发展的进程。所以,这个事情就简化成了两个部分:有深厚的 iOS 开发功底;在大前端时代里更好地适应。

谈谈对 iOS 开发的认识

iOS 应用开发是通用软件开发的一个方向。所以,做好 iOS 开发,需要 iOS 专业知识和通用软件专业知识。
上面提到“移动开发还真是什么人都能做”只是说这个行业入门的门槛相对较低而已,如果想在移动端开发有所作为,还是要努力掌握相关知识的。

UI

很多人可能对移动端开发有一些误解,甚至觉得做移动端就是画画 UI 而已。所以在知乎上会有“为什么最难不过二叉树的算法出现在面试题中都会被应聘者抱怨?”这样的问题。
额,不懂点算法,有的时候是画不好 UI 的。比方说,图片轮播器是一个很常见的 UI 组件,当在 GitHub 上搜索 时,我们会发现排名靠前的方案有些是通过将 original dataSource 重复几次的方式来生成一个较大的 UICollectionView, 从而以一种取巧的方式实现轮播的效果。这种方式可以只对 dataSource 做简单的算术运算,而不是使用深拷贝的方式,从而将开销维持在很低的水平上。不过,当我们懂得基础的数据结构之后,双向链表封装一下,可能会是一个更好的解决方案[1, 2]。

Runloop & Runtime

工作过几年的 iOS 工程师如果出去面试,一定会被问到关于 Runloop 和 Runtime 的问题,这是 iOS 工程师进阶必须懂得的知识。可能有些工程师会抱怨,在日常开发中又不会用到这些东西。这个观点是错误的。
Runloop 可以说是iOS 系统的灵魂。内存管理/UI 刷新/触摸事件这些功能都需要 Runloop 去管理和实现。理解 Runloop 并不是一定要在日常开发中调用 CFRunloopRef 或者 NSRunloop 相关的 API, 而是通过学习 Runloop 来理解 iOS 的运行机制,知道自己写下去的代码是怎样运行的,从而带给用户更好的体验。止步于死记硬背如下笔试题的工程师不是好工程师:

以下代码有没有什么问题?如果有?如何解决?
for (int i = 0; i < largeNumber; i++) {
    NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
    str = [str stringByAppendingString:@" - world"]; 
}

关于 ObjC Runtime, 很多人会冠以“黑魔法”的名称,听着就感觉很玄乎。做为普通开发者,必须纠正诸如“ObjC Runtime 就是 Method Swizzling” 这样片面的观点。在 category 里为 UIView 添加一个 property 是用到了 Runtime 相关的知识, KVO 也是用到了 Runtime 相关的知识。如果这些还不够,今日头条“iOS 客户端启动速度优化”了解一下,优化的 tips 包括减少 ObjC 类数量,减少 selector 数量,如果有可能就把 +load() 做的事情延迟到 +initialize 中执行。这些都是和 ObjC Runtime 相关的知识点,理解 Runtime 是能够解决很多实际问题的。

内存管理

“现在都是 ARC 时代了,还谈什么内存管理,让系统自己去管理吧。
ARC 减少了开发者花在内存管理上的时间。ARC 减少了 leaked memory, 而 abandoned memory 出现的场景多见于 self -> block -> self 这样的循环引用,会复制粘贴 weakSelf strongSelf 就能解决。”
在一个大型应用里,可能出现多个对象形成的循环引用,这种内存泄漏通常比较隐蔽,可能需要使用 Instrument 相关工具来定位问题,或者引入第三方库在 debug 阶段发现内存泄漏。而我相信这些检测内存泄漏的第三方库的作者是在深刻理解 iOS 内存管理方式之后才写出来的。所以,对于普通开发者来说,理解 ARC 的原理会提高日常开发的质量,增加对自己的代码的信心。

ARC 不是 iOS 内存管理的全部。深拷贝浅拷贝也是和内存管理息息相关的。实际工作中我曾经见过如下的代码:

-(RetObject *)someMethod {
    RetObject *localRet = [RetObject someInitMethod];

    return localRet.copy;
}

RetObject 并没有重写 copy 方法;并且对函数返回值做 copy 有什么意义呢?是试图保证 localRet 不会被释放掉吗?要不, autorelease 及其原理了解一下?

还有类似的代码
```
-(void)someMethod:(NSArray *)someArray {
NSMutableArray *anotherArray = someArray.copy;
// some execution code on anotherArray

top Created with Sketch.