swift 影响Xcode编译速度的注意事项

来源:互联网 发布:淘宝饰品详情页素材 编辑:程序博客网 时间:2024/05/18 01:32

、、、、、首先尝试次方发,看起来很有效

背景

随着 Xcode8 和 swift3.0 的正式到来,我开始着手将 swift2.3 的项目转到 swift3 (至于转换过程,这里不多做介绍,Xcode自带的转换工具,基本可以转换80%左右,剩下的自己慢慢调试即可。)但是,转到swift3只会,在用Xcode8编译,发现编译过程变得非常慢,哪怕打一个空格,都要重新编译很久。于是在网上查找解决办法,查找了半天各种方法全用,发现于事无补。 

有什么加一个 HEADERMAP_USES_VFS = YES () 这是地址,反正这个对我没用,感兴趣的话自己试试

针对这种情况,我就自己探索,出发点是排查哪些文件,哪些方法导致编译变慢?

探索Xcode编译,在哪些地方可以看到编译文件过程

通过多次编译查看,发现在Xcode左边栏,最后一个选项,是每次的编译运行等记录, 每行记录中都会有每次编译的文件编译记录。如下图

再次查看正在编译的一条记录(正在编译的记录后面有一个转圈圈的”菊花“),发现有图中1、2两种标识, 一个绿色:white_check_mark:说明,这是编译完成了,另一个是灰色→箭头,说明是正在编译;此时发现灰色一直停留在这里,指向图中的文件,左边的菊花一直转个不停, 编译卡住了。这样一来就找到编译是什么文件导致的那么慢。

排查文件中哪些代码导致的编译卡住?

排查的方法,采用比较笨的方法,暴力注释方法: 就是注释找到卡住的文件中的所有方法里的代码,不要注释方法,再次编译,此时发现该文件很快通过编译,然后在依次解开部分方法注释,再次编译,直到发现解开某个方法的注释后,编译再次卡住,那就说明那个方法中的代码写的有问题,然后在依次注释,解开注释,排查方法中的某一行代码的问题。 经过一番排查,我找到了我代码中卡住的部分代码,如下图红色圈中的代码 

在字典中的key对应的value,没有给直接的值,而是给出一个表达式,这样会导致Xcode8的编译速度极其慢,不知道为什么会出现这样的问题,之前的Xcode版本是没有的。

找到问题所在,解决问题就简单了,于是修改代码如下图:

修改之后,再次编译,重新回到了快速编译的时代了,再也不用等上5-10分钟不等了。

以上过程,是针对我遇到的问题而言的,不知道是否存在普遍性,如若你也遇到,刚好看了我的文章,也解决了问题,那就会心一笑吧,如若你也遇到了,但是没有解决问题,也请你会心一笑吧。

这是苹果开发者提到的Xcode8编译超慢的问题,我看了下,也做了对应修改,发现对我并没有什么用, 感兴趣的话进去看看吧

有时候自己慢慢去探索出问题的所在,也是很不错的体验。



、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

XCode默认使用与CPU核数相同的线程来进行编译,但由于编译过程中的IO操作往往比CPU运算要多,因此适当的提升线程数可以在一定程度上加快编译速度。
2. 将Debug Information Format改为DWARF
在工程对应Target的Build Settings中,找到Debug Information Format搜索这一项,将Debug时的DWARF with dSYM file改为DWARF。
这一项设置的是是否将调试信息加入到可执行文件中,改为DWARF后,如果程序崩溃,将无法输出崩溃位置对应的函数堆栈,但由于Debug模式下可以在XCode中查看调试信息,所以改为DWARF影响并不大。这一项更改完之后,可

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

一,非代码层面的检查和优化



、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、


前言


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.3msfunc 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.4mslet parameter : [String : AnyObject] = ["model" : model?.text ?? "",                               "cat" : cat ?? "",                               "name" : name ?? "",                               "number" : number ?? 0,                               "dog" : "dog"]

这次所需要的编译时间就已经缩短为了3235.4ms,通过手动指定类型,我们缩短了5s以上的时间让费

再紧接,我们继续观察,这个parameter的字典其实可以全部为String,根本用不到AnyObject。

指定特定的类型

//build time: 200.3mslet 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 : 9804msfunc calculateSize(view : UIView?) -> CGSize{    return CGSize(width: 10 + (view?.bounds.width ?? 0) + (view?.bounds.height ?? 0) + 22, height: 20)}//build time : 172msfunc 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.6msfunc arrPlusOperatos() {    let arr1 = [1,2,3]    let arr2 = [3,4,5]    result += arr1 + arr2 + [10]}//build time 8.6msfunc 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存在严重的编译过长问题。

更多:

  1. regarding-swift-build-time-optimizations
  2. swift-compiler-performance-tips-and-tricks
  3. why-is-swift-compile-time-so-slow

后续


框架上的提高编译性能:

  • 模块化代码,使用私有Cocoapods repository. 让不同模块以Framework或则.a文件的形式在项目里使用。如此每次编译的时候就只需要编译自己模块下的代码。其他模块的代码将会被编译后缓存,不需要重复编译。

其他一些Xcode优化包括:

  • Find Implicit Dependencies Off [1]
  • Build Active Architecture Only Yes On Debug [2]
  • Precompile Prefix Header set to YES [3]
  • defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks 4 [4]
原创粉丝点击