我使用的是Xcode 6 Beta 6。

这个问题已经困扰我一段时间了,但现在已经到了几乎无法使用的地步。

我的项目开始有一个相当大的65个Swift文件和一些桥接Objective-C文件(这真的不是问题的原因)。

似乎对任何Swift文件的任何轻微修改(比如在应用程序中几乎不使用的类中添加一个简单的空白)都会导致指定目标的整个Swift文件被重新编译。

经过深入的调查,我发现几乎100%的编译器时间是CompileSwift阶段,Xcode在目标的所有Swift文件上运行swiftc命令。

我做了一些进一步的调查,如果我只保持应用程序委托与默认控制器的编译是非常快的,但随着我添加越来越多的项目文件,编译时间开始变得非常慢。

现在只有65个源文件,每次编译大约需要8/10秒。一点也不快。

除了这篇文章,我还没有看到任何关于这个问题的文章,但这是一个旧版本的Xcode 6。所以我在想是不是只有我一个人有这种情况。

更新

我在GitHub上检查了一些Swift项目,比如Alamofire、Euler和CryptoSwift,但没有一个项目有足够的Swift文件来进行真正的比较。我发现唯一一个开始有一个像样的大小的项目是SwiftHN,即使它只有12个源文件,我仍然能够验证同样的事情,一个简单的空间和整个项目需要重新编译,这开始需要一点时间(2/3秒)。

与分析和编译都非常快的Objective-C代码相比,Swift真的感觉永远无法处理大项目,但请告诉我我错了。

更新与Xcode 6 Beta 7

仍然没有任何改善。这开始变得荒谬了。由于Swift中缺少#import,我真的不知道苹果将如何优化这一点。

更新Xcode 6.3和Swift 1.2

苹果增加了增量构建(以及许多其他编译器优化)。你必须将你的代码迁移到Swift 1.2才能看到这些好处,但苹果在Xcode 6.3中添加了一个工具来帮助你做到这一点:

然而

不要像我一样高兴得太快。他们用于增量构建的图形求解器还没有很好地优化。

事实上,首先,它不会查看函数签名的变化,所以如果你在一个方法的块中添加一个空格,所有依赖于这个类的文件都将被重新编译。

其次,它似乎基于重新编译的文件创建树,即使更改不影响它们。例如,如果将这三个类移动到不同的文件中

class FileA: NSObject {
    var foo:String?
}
class FileB: NSObject {
    var bar:FileA?
}
class FileC: NSObject {
    var baz:FileB?
}

现在,如果您修改了FileA,编译器显然会将FileA标记为要重新编译。它还会重新编译FileB(基于对FileA的更改,这是可以的),但也会重新编译FileC,因为FileB是重新编译的,这非常糟糕,因为FileC在这里从不使用FileA。

所以我希望他们能改进依赖树求解器…我用这个示例代码打开了一个雷达。

更新与Xcode 7 beta 5和Swift 2.0

昨天苹果发布了beta 5,在发布说明中我们可以看到:

Swift语言和编译器 增量构建:仅改变函数体将不再导致依赖文件被重建。(15352929)

我已经试过了,我必须说它现在真的(真的!)很好。他们极大地优化了swift中的增量构建。

我强烈建议你创建一个swift2.0分支,并使用XCode 7 beta 5保持你的代码是最新的。你会对编译器的增强感到满意(但是我想说XCode 7的全局状态仍然很慢并且有bug)

用Xcode 8.2更新

距离我上次更新这个问题已经有一段时间了,所以就在这里。

我们的应用程序现在大约有2万行几乎全是Swift代码,这还不错,但并不突出。它经历了迅速的迁移。在2014年年中的Macbook pro (2.5 GHz英特尔酷睿i7)上编译大约需要5/6m,这在一个干净的构建上是可以接受的。

然而,增量构建仍然是一个笑话,尽管苹果声称:

Xcode不会在发生小变化时重新构建整个目标。(28892475)

显然,我认为我们中的许多人在检查完这些废话后只是笑了(添加一个私有(私有!)属性到我的项目的任何文件将重新编译整个东西…)

我想让你们看看苹果开发者论坛上的这个帖子,上面有关于这个问题的更多信息(也感谢苹果开发者偶尔就这个问题进行的交流)。

基本上,人们已经提出了一些改进增量构建的方法:

添加一个HEADER_MAP_USES_VFS项目设置为true 禁用从您的方案中查找隐式依赖项 创建一个新项目,并将文件层次结构移动到新项目。

我试试3,但是1/2行不通。

在整个情况中具有讽刺意味的是,看看关于这个问题的第一篇文章,当我们第一次编译时,我们使用的是Xcode 6和swift 1或swift 1.1代码,而现在大约两年后,尽管苹果进行了实际的改进,情况还是和Xcode 6一样糟糕。多么讽刺。

我真的很后悔在我们的项目中选择了Swift而不是Obj/C,因为它涉及到日常的挫折。(我甚至切换到AppCode,但这是另一个故事)

不管怎样,我看到这篇SO帖子有32k+的阅读量和143个上升量,所以我想我不是唯一一个。坚持住,伙计们,尽管对这种情况感到悲观,隧道尽头可能会有一线光明。

如果你有时间(和勇气!)我猜苹果欢迎雷达的关注。

更新Xcode 9

今天偶然发现的。Xcode悄悄地引入了一个新的构建系统来改善当前糟糕的性能。您必须通过工作空间设置启用它。

已经尝试过了,但会在完成后更新这篇文章。不过看起来很有希望。


当前回答

在新的Xcode 6.3中,Swift编译时间得到了改进

Compiler improvements The Swift 1.2 compiler was engineered to be more stable and to improve performance in every way. These changes also provide a better experience when working with Swift in Xcode. Some of the most visible improvements include: Incremental builds Source files that haven’t changed will no longer be re-compiled by default, which will significantly improve build times for most common cases. Larger structural changes to your code may still require multiple files to be rebuilt. Faster executables Debug builds produce binaries that run considerably faster, and new optimizations deliver even better Release build performance. Better compiler diagnostics Clearer error and warning messages, along with new Fix-its, make it easier to write proper Swift 1.2 code. Stability improvements The most common compiler crashes have been fixed. You should also see fewer SourceKit warnings within the Xcode editor.

其他回答

编译器花费大量时间推断和检查类型。因此,添加类型注释对编译器有很大帮助。

如果你有很多链式函数调用,比如

let sum = [1,2,3].map({String($0)}).flatMap({Float($0)}).reduce(0, combine: +)

然后,编译器需要一段时间来找出应该是什么类型的和。添加类型会有所帮助。还有一个有用的方法是将间歇步骤放入单独的变量中。

let numbers: [Int] = [1,2,3]
let strings: [String] = sum.map({String($0)})
let floats: [Float] = strings.flatMap({Float($0)})
let sum: Float = floats.reduce(0, combine: +)

特别是对于数字类型CGFloat, Int,它可以帮助很多。像2这样的文字数字可以表示许多不同的数字类型。所以编译器需要从上下文中找出它是哪一个。

也应该避免使用像+这样需要花费大量时间查找的函数。使用几个+来连接几个数组很慢,因为编译器需要找出每个+应该调用哪个+的实现。因此,如果可能的话,使用变量a: [Foo]和append()代替。

你可以添加一个警告来检测在Xcode中哪些函数编译缓慢。

在目标的构建设置中搜索其他Swift标志并添加

-Xfrontend -warn-long-function-bodies = 100

对编译时间超过100毫秒的每个函数发出警告。

需要注意的一点是,Swift类型推断引擎在使用嵌套类型时可能非常缓慢。你可以通过观察各个编译单元的构建日志来大致了解导致缓慢的原因,这些编译单元花费了很长时间,然后将完整的xcode衍生命令复制并粘贴到终端窗口中,然后按CTRL-\以获得一些诊断。查看http://blog.impathic.com/post/99647568844/debugging-slow-swift-compile-times以获得完整的示例。

下面是另一种可能导致类型推断大幅减速的情况。合并操作符。

像这样改变台词:

abs(some_optional_variable ?? 0)

to

abs((some_optional_variable ?? 0) as VARIABLE_TYPE)

帮助我把编译时间从70秒缩短到13秒

我们已经尝试了很多方法来解决这个问题,因为我们有大约10万行Swift代码和30万行ObjC代码。

我们的第一步是根据函数编译时间输出优化所有函数(如https://thatthinginswift.com/debug-long-compile-times-swift/所述)

接下来我们写了一个脚本,将所有swift文件合并到一个文件中,这打破了访问级别,但它将我们的编译时间从5-6分钟缩短到1分钟。

现在这个功能已经失效了,因为我们询问了苹果公司,他们建议我们应该这样做:

在“Swift编译器-代码生成”构建设置中打开“整个模块优化”。选择“快速,整个模块优化”

在“Swift Compiler - Custom Flags”中,为您的开发构建添加“- onone”

设置这些标志后,编译器将一步编译所有Swift文件。我们发现合并脚本比单独编译文件要快得多。然而,如果没有'-Onone'覆盖,它也会优化整个模块,这是较慢的。当我们在其他Swift标志中设置'-Onone'标志时,它会停止优化,但它不会停止一步编译所有Swift文件。

关于整个模块优化的更多信息,请查看Apple的博客文章https://swift.org/blog/whole-module-optimizations/

我们发现这些设置允许我们的Swift代码在30秒内编译:-)我没有证据证明它如何在其他项目中工作,但我建议尝试一下,如果Swift编译时间对你来说仍然是一个问题。

注意,对于你的应用商店构建,你应该去掉“-Onone”标志,因为优化建议用于生产构建。

对于Xcode 8,转到项目设置,然后编辑器>添加生成设置>添加自定义设置,并添加以下内容:

SWIFT_WHOLE_MODULE_OPTIMIZATION = YES

对于一个40KLOC的swift项目,添加这个标志使我们的清洁构建编译时间从7分钟下降到65秒,这是奇迹。也可以确认2个朋友在企业项目上也看到了类似的改进。

我只能假设这是Xcode 8.0的某种错误

编辑:在Xcode 8.3中,它似乎对某些人不起作用了。