主要分享了Airbnb使用Swift的一些经验,Airbnb的Swift代码的使用已占大约80%~90%,算是业界比较多的了。
Swift的主要特性以及在Airbnb的应⽤
安全
Swift使用Optional Variable
区分是否为空,T
与T?
为不同的两个类型,使用Wrap
和Unwrap
进行转换
对于let vs var
,尽量使用不可变的let
Swift提供了高级枚举类型,使得enum
变得异常强大,可以支持之前传统上只有类才可以支持的特性,比如计算属性、实例方法、初始化、扩展、协议等。
Protocol Oriented Programming
这个之前没有注意,但事实上Swift整个语言与OC已经截然不同,OC依然是传统的面向对象编程,而Swift已经是面向协议的编程。
可以通过ASIHttpRequest
与AFNetworking
各自实现网络请求的方式来比较
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDidFinishSelector:@selector(requestDone:)];
[request setDidFailSelector:@selector(requestWrong:)];
[request startAsynchronous];
ASIHttpRequest
是面向对象的,你需要实现一个对象的实例,设置属性,调用对应的方法。
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:@"www.olinone.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
AFNetworking
是面向协议的,你可以看到这里只需要对方法进行直接调用,而不需要了解对象的属性。
我们自己在使用Swift的转换中,似乎还没有意识到这一点。
泛型
在可能处理多种类型的地方使用泛型:
这里类型可能会有如下三种:
CarouselComponent<HomeCardComponent>
CarouselComponent<ExperienceCardComponent>
CarouselComponent<GuidebookComponent>
其他特性
Memory Management:全部是ARC
Less Code:没有.h .m
Faster: Struct 使用栈,Class使用堆;Swift的Copy Write在堆上面
Tuples:支持多元组,不同类型的集合
Access Control:OC的动态特性破坏了访问控制,而Swift不支持动态特性
Airbnb遇到的问题以及解决方案
编译时间
Airbnb在开发的过程中编译的时间越来越长,发现是由于Swift进行“类型推导以及类型检查”时耗费了大量的时间。
如何调试编译时间,发现占时过多的方法:
target -> Build Settings -> Other Swift Flags 添加编译设置
-Xfrontend -debug-time-function-bodies
在“times.txt”文件中查看各个方法的耗时,下面的命令可以排序
xcodebuild -workspace yourWorkspaceName.xcworkspace -scheme schemeName clean build 2>&1 |egrep "d.dms"|sort -nr > times.txt
根据检查出来的问题,总结了一些有针对性的解决方案:
- Use CI system to monitor the build time.
- Educate engineers to provide type information as much as possible.
- Use Lint to prevent engineers from common issues.
- Offline type inference (Use SourceKit).
BUCK
使用BUCK编译系统来提高编译速度(并行编译、增量编译、缓存编译中间结果、开源)
Whole Module Optimization
全模块优化WMO的原理就是在编译项目时,将所有源代码合起来作为一个整体进行分析和优化,与单文件优化SFO(Single File Optimization)相区别,通过排除死函数、去inline函数调用等优化最终的目标文件,可以数倍的提高性能。
这篇文章评测 Swift 3.0 项目编译优化选项对编译速度的影响研究了三种模式的编译时间:
- None: 102s
- Fast, SFO: 122s
- Fast, WMO: 89s
这里比较有趣的一点是WMO作为优化了的编译方式,所花的时间比不优化的还要快,官方的解释是既优化了性能,又优化了编译速度(好吧~)
WMO在Release默认打开,但是在Debug默认关闭,这是因为WMO的增量编译做的不好:改动一个文件后,编译器还得把所有的文件再编译一遍。
此外,针对WMO,这篇文章优化 Swift 编译速度有更深入的分析:把所有Model文件全部合并到一个文件去编译,这样做的效率提升超过了WMO。
这是因为,WHO除了合并文件之外,还会在预编译阶段做一些事情,包括:检测并去掉没有被调用的方法和类型;给没有被继承的类或方法加上final
标签,优化为静态调用或者内联进去。
这些优化可以大幅提升程序的效率,但是导致在编译阶段每合并一个文件,就会遍历所有文件进行一次检查,使得编译时间过多。 这里采用了一个比较“Tricky”的方法做到只合并文件,不做优化:
这种方法解决了上面的问题,不过之前增量编译的问题同样存在:增量编译的颗粒度从File级别增大到Module级别。
Airbnb这里采用的就是这种方法,不过,这种方法对Airbnb很有用,是因为:Airbnb有70个左右的类库,这在某种程度上降低了增量编译的颗粒度级别。但是对于我们这种只有一个Target的工程来说,好处不大。
启动性能
Airbnb通过下面的方法调试启动性能:
动态库合并
Dynamic Library Merging
- Consolidate Assets (Pre)
- Build pods filelist
- Build internal libraries filelist
- Consolidate Assets (Post)
- Remove unnecessary embbed libraries
Airbnb通过这些操作,减少了12M的安装控件,以及降低了50%的"Pre-main Time"
Swift和Objective-C混用
提到了两点
- 一定要在oc代码里面正确的标注nullable
- 增量编译时间
为了与Swift中可选对象对应,OC提供nullable
, nonnull
问题
1 改一个文件,从0开始编译,增量编译有没有影响?更慢?
所有的文件都放到一个里面编译,不加选项的话,就是单个文件。所以增量编译是没有优势的,但是要rebase的话不一样。 Airbnb有七十多个类库,所以可以使用这种方法。
2 设计方法的时候,如何取舍加空或不加空?
不加空最好:避免出错,性能
3 SwiftLint是一个用来规范代码的工具