小猪的博客

2016年末总结和近期感想

年后的最后一天,也是该总结一下这一年的成长了。

从校园到实习

软件专业的大三学年,如果你不考研留学的话,说起来都是会走实习加校招的流程。自己这年印象最深的,也就是整个下半年的实习,面试,以及现在的实习了。

大三上的实习当时自己错过了机会,只进入了一个普通的互联网公司,但是也学到了更多校园中无法见到的东西(比如多移动App多Project管理,MapKit,React Native),开阔了一点眼界。真正的下半年好几个月都在忙着校招面试相关的东西,从中也总结了一些经验吧(虽然不知道对社招是否有帮助)。然而到现在,从南京来到了北京,提前过来头条这边实习。

实习开始都是兴奋和好奇的,因为可以看到公司的各种设施,福利,还有认识各式各样的人。来这边也是专门拉了两个同公司的应届生一起来,也挺愉快的。公司离得近,组里的人也都比较厉害(至少都是我请教别人教育的情形),虽然上班累了些,但是感觉自己成长也是非常的大,很难想象假如不来实习,全靠自己摸索,明年直接毕业过来上班自己是什么样的水平。

近期工作感想

来到头条这边也刚刚好一个月了把。这期间最大的感触,就是自己的工作和产品将会面对的是上千万级别的日活用户,而且不同于之前的实习那样一人负责非常小的业务,看看需求,写写代码,测试简单过一遍,直接就git提上去了。在这里,整个工作流都是有专门有PM跟,并且开发都是自己checkout分支不断merge,节奏更是快了不少。提测流程也是更加的严格,因为现在你开发的产品不再是发展启蒙阶段的不完成品,一个需求如果不能覆盖好完整的边界情况,宁愿不要这个功能,也不能带着一点侥幸心理上线。在千万级别使用量的冲击下,任何情况都可能发生。因此一定要做好特殊情况的处理。

同时,在产品达到一定规模,我也越发察觉到AB测试、开关、日志监控和报警功能的重要性了。移动App不同于Web前端,一旦发一个版出去,那么就得考虑对应的兼容性和留存率。一旦出现线上Bug,立即发现并且关闭对应的功能是非常重要的解决方式,之后可以再考虑使用热修复技术来对旧版本进行处理。来这里可以看到各种功能都考虑了埋点,日志,还有AB测的开关。在校园期间自己接触开发流程中少了很多关于这方面的意识,正需要借此多加注意。

工作中的技术问题

AVFoundation的注意

由于有需求要做录像和人脸识别相关的东西,因此自己才真正使用了AVFoundation这个Apple提供的媒体库。虽然你可以简单的在SF上搜到一堆答案,但是真实的开发有很多潜在的问题。尤其是这种涉及到硬件层面的API,稍不留神就会报Crash。

比如最常见的问题,用来保持一个输入设备到输出设备的会话,会用到Session,而AVFoundation中的这个AVCaptureSession的很多方法,都是阻塞的。简单的启动录制流- startRunning方法也是阻塞线程的(这一点在文档中有说明,需要注意),要使用一个单独的concurrent queue来处理,并且还要记得dispatch到mainQueue上来供外部UIKit的使用,不然其实看起来表面上UI是正常的,但是整个UIResponser就会受到影响。其他的还有比如修改输入流,输出流的配置(比如视频流的方向,帧率之类)的配置时候,也得配合- beginConfiguration- commitConfiguration,不然会出现线程冲突的问题。

还有的坑点,不同于ImagePicker,AVFoundation的采集回调也需要你手动指定线程,比如AVCaptureAudioDataOutputSampleBufferDelegate可以用来持续的采集buffer来生成对于的Image,进而用于OpenCV或者自带的CIDetercor做人脸识别的持续追踪。但是在调试时候没有注意到回调在非主线程而进行了UI操作导致各种的问题,浪费了很多时间,看来自己理论上的知识和实际的经验还需要再磨练一些。

手动处理单向数据流的坑

因为之前一直在看一些React呀,Rx系列的东西,所以见到一个需求看到对应的页面状态不多,就想试试用这种单项绑定数据流来写。由于App对包大小比较敏感,也没有引入ReactiveCocoa,自己参考着React的Redux一般,放了一个单独的Store Model来作为状态的Store,同时定义了十几种状态。所有的View层(View和ViewController)的交互导致的状态变化,模型变化必须通过ViewModel的调用才能修改Store Model,而Model的数据校验也都丢到了ViewModel里面。这就形成了类似Redux那种View(View/ViewController) -> Action(ViewModel) -> Store(Model) -> View的单项数据流动。

看起来一切都很美好,但是实际写起来就发现简单照搬的单向数据流的问题了。随着需求的变动,经常会有那种需要你这一个ViewController实例的某些操作后同步到其他ViewController的UI上(比如用户交互或者网络请求的响应改变了Store Model,在导航栈上前几个页面也需要刷新UI),按照最笨的写法那种在当前的ViewController绑定一个callback或者delegate回调肯定是不好的(虽然我之前也这么写过,参考了Android的Intent也是这样子做的)。但是参考的Redux的管理方式下,其实是不会特别考虑这种情况的,因为React的render方法会在你的props变动的时候触发,你最多可以判断一下是不是需要rerender就行了。然而在原生App中,你几乎总是避免rerender,只是改变某些UI的属性罢了。

最开始我的解决策略是放到了ViewWillAppear中,强行Switch了当前的状态来传递Model到View层。但是这种情况下会导致把你的正常View逻辑也写的特别乱(得有各种判断)。更可怕的是当你的ViewController并不是一对一绑定给真正的页面,而是一个ViewController模版对应五六种类似页面布局的ContainerView时,你就可以发现代码中会有好几处不同的Switch。这并不是一个好的架构。现在的解决方案是通过ViewModel绑定了所有ViewController的弱引用,在ViewModel通用的状态处理逻辑中判断,并且直接调用ViewController的updateViews,继而走到真实的某个View的UI刷新方法中。但是这样做的缺点就是你的ViewModel逻辑会同时包括了第一次初始化和更新的东西,这就是比较让人麻烦的地方。也试过KVO来绑定了View,但是由于我的ViewController本身就是个通用模版,绑定的真实实例和销毁时机也得判断,所以Pass……

现在想想,ReactiveCocoa这种双向数据绑定解决方案,确实是实现Reactive Programming所需要的。没有了数据绑定,整个开发流程的效率深知会降低到手动处理状态之下。绑定后开发者不需要再手动处理update的时机,只需要提前在view中定义一套映射,viewModel只需要处理Model的变化,也不需要管是否需要回调viewController的update情况了。

明年的简单打算

基本上,明年就是真正的毕业生和在职人员了。公司不会像学校那样随心所欲,估计到时候自由时间将会少了很多。技术方面,感觉自己在移动开发这条路上还是可以走下去的,只不自己重点想关注偏工程和中间层技术的领域,比如之前看的一些网络中间件,模型保护,日志监控之类。虽然之前发了一部分关于Core Animation的东西,但是发现UI这部分真的并不是特别感兴趣,自己的目标也就在能够完成基本的需求下和简单的绘图动画即可。

还有的方面,新的一年要多接触接触领域圈子内和圈子外的人。老实说自己的交际能力还是非常欠缺的,无论是做移动的技术圈子,还是同学朋友的生活圈子,都需要更为关注。在学校能够只关心眼前事,但是到社会上是远远不够的。一个人的能力再强,处境再好,总比不上周围的朋友和同事的协力。

就这样吧,2016将要过去,明年将是一个更新,也更加充满机遇的一年。