Gonna be Swifter

Overview

实习了接近三个月时间,做了三个工作,第一个用Objective-C编写了类似朋友圈的功能,第二个用Swift 2.2完成了新应用,兼容到iOS 7,第三个是毕业设计,用Swift+Objective-C,但是客户端、服务端和数据抓取全写了。Swift进化到2.2版本后,依旧遍地是坑(至少在写代码的过程中我总是时常地接近崩溃),或者说相比使用Objective-C,开发者的用户体验还不是很好,特别是那种遇到自动补齐卡住,然后全屏代码泛白,感觉脆弱的神经都要完全崩溃了。在等待毕业的日子里,回顾下自己的工作。

第一个项目

第一个项目用了JSONModel,Masonry这两个重要的第三方库,一个解决了数据建模和序列化,另一个解决了界面布局问题,另外还需要讨论一下性能优化和错误处理,如果想起什么,就写一点。

JSONModel

重度使用JSONModel对于我来说还是第一次,感觉它实在太方便了,让程序员直接跳过了处理JSON数据这个麻烦的问题,但是在看过JSONModel与其他类似工具的对比后,发现它也并不是很完美,在转换效率和错误处理上远远落后。

正常情况下,一旦API接口完成后,数据格式就会确定不变,因此出错概率也很低,而对于转换效率,当硬件性能提升时,这并不是问题,何况密集的JSON数据转换场景在App中的发生概率也是接近于零,对于单次数据转换,不同库的性能差异就很有限,这个时候,大概关注点就需要转移到库的活跃度,维护者,文档化之类的东西,毕竟有ASI的例子在前头摆着。总之,错误处理需要用概率来评价,性能度量也需要考虑使用场景。

Masonry

自动布局的最大优点也许是转换了坐标概念,将绝对坐标替换为相对坐标,赋予界面布局以对象的概念,就像NSOperationQueue与GCD的关系,但性能损耗是有的。复杂的布局需要更多的CPU资源,如果是应用在cell中,还需要不断刷新布局,相对于能够一次性缓存的手工布局来说,每次刷新都在消耗计算资源。如果是内容比较复杂的cell,不可避免地会发生卡顿,最初没有缓存图片时,即使是在6s上,也一样发生卡顿,但经过优化,最后在5c上也运行流畅。

性能优化

在计算密集的地方,就需要考虑性能优化了,比如视图刷新,图片加载等,否则当产品迭代,增加功能后,可嫩就要面临无法修正的卡顿状况。目前我还主要停留在多线程和缓存两个方案中。尽可能将计算过程分发到多个线程中,让需要计算的地方充分利用CPU资源,不过这也会带来一个问题,线程同步,其他线程中的运算结果往往需要在主线程中回调,而有些例如自动布局计算,只能在主线程中执行,所以除了多线程方案,还可以考虑更低层的技术来控制计算过程,比如RunLoop,互斥锁,自旋锁和信号量......至于缓存,一般遵循内存->闪存->网络的缓存方案,缓存最大的问题是数据同步,如果是多层嵌套的视图,数据传递和数据同步会是一个很大的问题。

错误处理

可能发生错误的地方,一定会发生错误。第一个项目最让人痛苦的一点是,在我调试bug的时候,本来不该出错的地方也出错了,因为服务端API还不稳定。而用Objective-C写代码时,Xcode并不会象Swift一样,提醒可能抛出异常的地方,结果就是数据联调阶段中,我完全崩溃了。也许是因为初次接触,也许是因为项目本身在选择技术时就有问题,最后锅还是会扣到开发身上。编写处理所有错误的代码时不可能的,还是象之前提到的,需要考虑错误产生概率和应用场景来制定应对方案,不过这两个都需要经验积累。

小结

第一个项目后,最大的收获是理解团队合作,毕竟之前最喜欢的就是一个人完成全部任务。合作过程中也不是没有矛盾,比如当新增了一个时间戳时,由谁来处理数据?当时由于后端的数据不稳定,我们App开发基本处于停滞状态,只能使用模拟的数据,不断测试,而真实的数据情况又和模拟的不同,很多情况下需要依赖具体数据来刷新不同视图,甚至处理逻辑。如果在App开发前,后台数据能够完全准备好,想必是极好的,问题是新需求提出后,大家都是一起开工的,让前端一直等待也是不科学的,而有些需求,甚至连细节也很模糊,到后期改改改的情况也无法避免。既然有那么多不确定的因素,最好的解决方式,还是让自己能够做到独立开发,能够详细理解分工的话,就能更好地应对不同的情况。

第二个项目

或许一开始用Swift来开发就是个错误,Swift是个很好的语言,但这还是无法阻止混编时,所有的代码颜色全部变白,然后无休无尽地卡顿,然后最近又收到一个噩耗,Swift 3.0将不会兼容Swift 2.2,感觉十分悲伤。

自动补齐

在这个项目中,为了兼容到iOS 7,踩了很多坑,首先是继续使用AFN,JSONModel,使用SnapKit代替Masonry,下拉刷新使用了MJRefresh,不过最大的问题也许在于使用静态库尔没有使用Framework,结果掉进了自动补齐的噩梦。许多Objective-C的block语法自动转换到Swift时,总是会发生奇怪的问题,而一些通过Runtime特性添加的Category属性也是各种无法补齐,Objective-C上又长又详细的方法和参数列表这个时候完全变成噩梦了。到最后只能依赖复制粘贴+编译运行来检查语法错误,因为Swfit总是从入门到重学。

交互、设计与实现

因为是全新的项目,应用界面进行了重新设计,但交互稿是最先出的,设计时没有检查交互过程中的逻辑错误,等到全部实现后,测试阿姨发现好像功能有点问题......然后改改改。交互是动态的,而UI在设计过程中,至少每个独立的页面是处于静态的,结果开发人员忽略了界面动态刷新时的样子。

实现过程中,因为渐变色也踩了很多的坑,当时需要下拉时调整渐变色效果,给人一种动感,不过这需要检测下拉状态,于是又掉进下拉刷新的坑。当一个函数里需要协调两种功能时,问题就涌出来了。如果能够提早预览最后的设计效果图,或许情况会有所改善。因为交互和UI常常是同时改变的。

协作

协作还是一个问题,现在的应用基本上都需要同时开发安卓与iOS两个版本,这时问题来了,UI的设计是需要分为安卓和iOS两个版本呢?还是还是说要两者统一?我是拒绝后者的,因为可能出现安卓能做而iOS不能,或者iOS可以而安卓不行。这里原因各种各样,可能有的时候前者有现成的实现,而后者没有,反之亦然,或者二脸懵逼的状态下跟着产品走,其实如果客户端的开发人员能够全部都了解一些对方的开发,协作效率会更高一点。

小结

第二个项目开始,就是拼命码字了,最大的收获也许是写了很多的Swift代码,踩了很多坑,然而这并不代表能够有很多成长,在磨练硬实力的同时,软实力还是老样子。

第三个项目

最后一个项目就是毕设了,从很早开始设计,到3月底动手写客户端,中间隔了很长的时间,尽管有很多的想法,最后还是在时间逼迫下,匆匆完成。这个项目的最大问题就是服务端开发了。一开始计划用Python写,开始实习后,经过一段时间的Java学习,又打算用Java了,最后跳回Swift,使用Perfect写了服务端。开发过程中遇到的问题,也基本上都集中在设计上,编码实现的过程反而没有太大的问题,或许问题就在于独自开发,在不清楚产品的前提下,首先就用技术来测试下,导致每个功能基本上都是逐一测试,编码,重新设计,再编码,也许是开发人员的思维习惯,在想到功能时,就先想到界面,然后开始......效率很低。第三个项目可以总结的,差不多都是问题。

界面布局

由于没有点亮UI设计技能点,所以在开发时,会倾向于用代码编写界面后慢慢调试,这个效率太低了,可以学习使用一些UI设计人员的工具,比如Sketch,假如完成了设计,实现过程中还是有一些问题。

纯代码 vs StoryBoard或者xib的问题一直都在,目前是采用纯代码,因为还未尝试过xib的协作开发,特别是遇上那种不小心打开xib后再提交代码,change log里的改变多到可怕。纯代码用自动布局和手动布局都有各自的问题,从实现和维护上讲,自动布局的维护性更强一些,而实现上采用Masonry也好,SnapKit也罢,添加约束时的代码量也很大,如果是一个复杂的视图,光是完成界面布局的部分可能就占据了一半的代码量,而使用手动布局需要自己计算,在控件密集的部分,会消耗大量的脑细胞,但它效率高,在按照数据内容改变视图的时候,能够更好地控制可以优化的部分。

服务端

用Perfect开发的服务端太简单了,每一个请求都是直接查数据,因为目前没有缓存工具,对于sql语句也没有做进一步处理,如果查询一个emoji表情,服务端会马上崩溃。目前考虑用Python来做一个。

数据

数据是从网页中直接扒下来的,简单粗暴,使用Objective-C进行XML解析,在iMac上花了大概半个小时处理了60万个网页,内存占用量不多,瓶颈都在CPU上。文本数据全部使用JSON格式,包括目录、简介、小说文本,而封面和插画则是用wget全部下载,最后总量约15GB,已经超出了我小VPS的容量了,数据更新没有做,下一步应该研究下爬虫,用一种更优雅的方式动态更行。

小结

虽然完成项目时还是很开心的,但完成后就没了继续更新的动力,公司的项目有需求在驱动,而自己的项目就需要自己来寻找驱动的理由。这个项目里基本涵盖了独立开发的各个流程,从动手开始,就不停在各种身份中切换,产品、UI、开发......累得慌,但很有意思。

接下里的工作

好好学习,天天向上,赶上Swift的更新速度。