Swift 编译时间折半优化

2020-03-30  本文已影响0人  郑一一一一

前言

在 Swift 项目过程中,随着代码量的不断增加,每次调试的编译速度越来越慢,在 debug 下编译项目竟然需要 6 min 以上。遂开始研究如何缩短编译时长。

如何获取精确的编译时长?

编译前,请执行以下两个步骤:

  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?

代码优化

代码层面的优化,我们可以依靠 Xcode 找出耗时较长的方法,进行针对性优化。

配置

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 的代码处,我们就可以根据下面进行针对性修改啦。

1 指明变量类型

普通变量

// 修改前
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"]

闭包

// 修改前
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 来代替

// 修改前
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
上一篇下一篇

猜你喜欢

热点阅读