Swift 編譯時間折半優化

前言

在 Swift 項目過程當中,隨着代碼量的不斷增長,每次調試的編譯速度愈來愈慢,在 debug 下編譯項目居然須要 6 min 以上。遂開始研究如何縮短編譯時長。git

如何獲取精確的編譯時長?

編譯前,請執行如下兩個步驟:github

  1. 清空 derived data 文件夾中全部文件(推薦一個清理工具 Cleaner for Xcode ,能夠在 Mac App Store 搜索下載到)
  2. Xcode 中 clean(快捷鍵:cmd+shift+k)
  3. 可以讓 Xcode 顯示編譯時間,終端輸入
//  Xcode 顯示構建時長設置
// 在 terminal 中輸入
defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES
複製代碼

Xcode 配置優化

// 1
Build Settings -> Swift Compiler - Code Generation -> Optimization Level 下 Debug 修改成 None
// 2
Build Settings -> User-Defined 下 Debug 增長 SWIFT_WHOLE_MODULE_OPTIMIZATION = YES,其它諸如 Release 配置請使用 NO
// 3
Build Settings -> Build Options -> DEBUG_INFORMATION_FORMAT 下 Debug 修改成 DWARF
// 4
Build Settings -> Architectures -> Build Active Architecture Only 下 Debug 改成 YES
複製代碼

關於 SWIFT_WHOLE_MODULE_OPTIMIZATIONOptimization Level 的區別,如下連接有詳細說明。簡單來講前者是從全局層面對全部代碼進行優化,然後者是針對每一個 file 的優化。 What is the difference between User Defined SWIFT_WHOLE_MODULE_OPTIMIZATION and Swift Optimization Level?express

代碼優化

代碼層面的優化,咱們能夠依靠 Xcode 找出耗時較長的方法,進行鍼對性優化。swift

配置

Xcode -> Build Settings -> Swift Complier - Custom Flags
Debug 下增長

// 1 方法體時長超過 100 ms
-Xfrontend -warn-long-function-bodies=100
// 2 類型推斷時長超過 100 ms
-Xfrontend -warn-long-expression-type-checking=100
複製代碼

添加如上配置之後,只要時長超出100 ms,Xcode 中便會顯示 warning。這裏的時長能夠根據實際狀況進行調整。數組

Xcode層面修改

代碼層面修改

針對產生 warning 的代碼處,咱們就能夠根據下面進行鍼對性修改啦。xcode

1 指明變量類型

普通變量bash

// 修改前
var name = getName()
// 修改後
var name: String = getName()
複製代碼

數組閉包

// 修改前
let array = ["a", "b", "c", "d", "e", "f", "g"]
// 修改後
let array: [String] = ["a", "b", "c", "d", "e", "f", "g"]
複製代碼

閉包app

// 修改前
let sum = [1, 2, 3].map { String($0) }.flatMap { Int($0) }.reduce(0, +)
// 修改後
let sum = [1, 2, 3].map { (num: Int) -> String in String(num) }.flatMap { (str: String) -> Int? in Int(str) }.reduce(0, +)
複製代碼

2 拆分 chain closure

// 修改前
let sum = [1, 2, 3].map { String($0) }.flatMap { Int($0) }.reduce(0, +)
// 修改後
let numbers = [1, 2, 3]
let stringNumbers = numbers.map { String($0) }
let intNumbers = stringNumbers.flatMap { Int($0) }
let sum = intNumbers.reduce(0, +)
複製代碼

3 避免空合運算符 ??

可使用 if let 來代替less

// 修改前
let name = string ?? ""
// 修改後
if let name = string{ 
 /* string has value */
}else{
 /* string is nil*/
}
複製代碼

4 避免三元運算符 ?

// 修改前
let letter = isFirst ? "a" : "b"
// 修改後
var letter = ""
if isFirst { 
  letter = "a"
} else {
  letter = "b"
}
複製代碼

5 不要使用 + 來拼接字符串、數組

字符串

// 修改前
let url = "https://google.com/" + "path/" + "anotherpath"
// 修改後
let url = "https://google.com/\("path")/\("anotherpath")"
複製代碼

數組

// 修改前
let systemNames = (0...2).map{ String(format: localizedFormat, systemOptions[$0]) } + [NSLocalizedString("everything", comment: "")]
// 修改後
var systemNames = systemOptions.dropLast().map{ String(format: localizedFormat, $0) }
systemNames.append(NSLocalizedString("everything", comment: ""))
複製代碼

6 避免 if else if 中作太多運算

// 修改前
if number == 60 * 60 {}
// 修改後
let number: Double = 60 * 60
if number == 3600 {
}
複製代碼

7 避免無心義的類型轉換

// 好比變量 a 已是 CGFloat 類型,不要再強轉 CGFloat(a)

// 修改前
return CGFloat(M_PI) * (CGFloat((hour + hourDelta + CGFloat(minute + minuteDelta) / 60) * 5) - 15) * unit / 180

// 修改後
return CGFloat(M_PI) * ((hour + hourDelta + (minute + minuteDelta) / 60) * 5 - 15) * unit / 180

複製代碼

8 使用 let private final

使用 Final 的分析

9 使用自定義運算符(尤爲是含有泛型參數)

結果

最終經過 Xcode 和代碼兩個層面的優化,編譯時間從原先的 360 s 左右縮減到 190 s。哇哦!

其它編譯時間分析工具

BuildTimeAnalyzer-for-Xcode

參考連接

  1. Sundell Improving Swift compile times
  2. Improve your Xcode (Swift) Compile Time
  3. Optimizing Compilation Time for Swift Code
  4. How to make Swift compile faster
  5. Speed up Swift compile time
  6. Regarding Swift build time optimizations
  7. Swift build time optimizations — Part 2
相關文章
相關標籤/搜索