主要分享了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是一個用來規范代碼的工具