Swift/Xcode編譯慢, 加快編譯速度, Swift/Xcode Long Compile time
作者:yuan
- iOS
- Swift
- Xcode
前言
Swift作為一個新興的語言,有著蘋果Dad(dy???)的支持與良好的社區環境。于是乎大家都開始慢慢嘗試在項目中使用Swift。在我們的項目中就有大量的Swift,但是過慢的編譯時間真的是killing us。 完整的編譯一次可能需要15到20分鐘,完全不能忍。
先說結果,在我的電腦MacPro上,項目編譯時間直接從以前20m41s
縮短了10m16s
。
而這10分鐘僅僅只是從的代碼層面帶來的效果
定位問題
國外友人Robert已經為我們寫好了一個Swift編譯時間定位的工具【請戳這里】。 方便好用,可以立竿見影的找到問題代碼。
ps:以前也用XCTOOL但是xocde8已經不再支持build了,非常可惜。
代碼優化
指定類型與拒絕泛型
原碼:
var model : UILabel?
var cat : String?
var name : String?
var number : Int?
//build tiem : 8740.3ms
func sendData() {
let parameter = ["model" : model?.text ?? "",
"cat" : cat ?? "",
"name" : name ?? "",
"number" : number ?? 0,
"dog" : "dog"]
print("send request with parameter:\(parameter)")
}
這段代碼所需要的編譯時間是8740.3ms
,就這么一個字典定義,我們竟然浪費了8秒!
指定類型之后我們再來看看
//build time: 3235.4ms
let parameter : [String : AnyObject] = ["model" : model?.text ?? "",
"cat" : cat ?? "",
"name" : name ?? "",
"number" : number ?? 0,
"dog" : "dog"]
這次所需要的編譯時間就已經縮短為了3235.4ms
,通過手動指定類型,我們縮短了5s
以上的時間讓費
再緊接,我們繼續觀察,這個parameter的字典其實可以全部為String,根本用不到AnyObject。
指定特定的類型
//build time: 200.3ms
let parameter : [String : String] = ["model" : model?.text ?? "",
"cat" : cat ?? "",
"name" : name ?? "",
"number" : "\(number ?? 0)",
"dog" : "dog"]
這次所需要的編譯時間就已經縮短為了200.3ms
,把AnyObject ->String ,我們縮短了2s
以上的時間讓費!!
所以如果你可以一種類型搞定,請千萬別寫AnyObject
!!!
通過指定正確的類型我們從8740ms
的編譯時間縮短到了200ms
!!!!
運算時nil
保護抽離
//build time : 9804ms
func calculateSize(view : UIView?) -> CGSize{
return CGSize(width: 10 + (view?.bounds.width ?? 0) + (view?.bounds.height ?? 0) + 22, height: 20)
}
//build time : 172ms
func calculateSize(view : UIView?) -> CGSize{
let width = view?.bounds.width ?? 0
let height = view?.bounds.height ?? 0
return CGSize(width: 10 + width + height + 22, height: 20)
}
這段代碼編譯了9804ms
,只是因為我們在運算的時候一并加入nil的保護。如果我們拆離nil保護,編譯時間縮短了98.3%
- 使用三目運算(Bool ? a : b)時也非常耗時,但還沒有到非常嚴重的程度,一個三目可能需要額外的
100ms
到200ms
編譯時間- 當你在字典中使用nil保護時,也可能造成極長的編譯時間,有時候甚至會長達
20s
.但不是每次都出現。我理項目時就通過BuildTimeAnalyzer發現了很多這樣的問題。比如:["model" : model?.text ?? ""]
. 在通過把他們強制轉化成想要的類型String
后得到解決:["model" : (model?.text ?? "")as String]
。 暫時還不知道為什么。 猜測是因為model?.text
的text屬性是一個可選型, compiler花費了很長的時間來確定到底是Optional(String)
還是String
.但又不是每次都出現,非常奇怪。
少用+
、+=
運算符
//build time 1400.6ms
func arrPlusOperatos() {
let arr1 = [1,2,3]
let arr2 = [3,4,5]
result += arr1 + arr2 + [10]
}
//build time 8.6ms
func arrPlusOperatos() {
let arr1 = [1,2,3]
let arr2 = [3,4,5]
result.appendContentsOf(arr1)
result.appendContentsOf(arr2)
result.appendContentsOf([10])
}
盡量少的使用+
、+=
號來合并參數, 在項目中有一些array的合并編譯時間高達5000ms
.
對于
String
也是一樣的,String
使用\(value)
來合并值,或API給的append.
總結
上面的幾個問題是在整理項目(Swift2.3)中,特別明顯影響編譯速度的點:
- 指定類型、拒絕泛型
- 運算時nil保護抽離、少用三目運算
- 少用+、+=運算符
縮短了我們接近50%的Swift編譯時間。
具體大家可以用BuildTimeAnalyzer來查看項目哪些func
存在嚴重的編譯過長問題。
更多:
- regarding-swift-build-time-optimizations
- swift-compiler-performance-tips-and-tricks
- why-is-swift-compile-time-so-slow
后續
框架上的提高編譯性能:
- 模塊化代碼,使用私有Cocoapods repository. 讓不同模塊以Framework或則.a文件的形式在項目里使用。如此每次編譯的時候就只需要編譯自己模塊下的代碼。其他模塊的代碼將會被編譯后緩存,不需要重復編譯。
其他一些Xcode優化包括: