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
相关文章
相关标签/搜索