我使用的是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.1中没有任何工作-当我添加了大约100个Swift文件随机挂在构建和/或索引时。我尝试过模块化的选择,但没有成功。

安装和使用Xcode 6.4 Beta对我来说很有效。

其他回答

也许我们无法修复Swift编译器,但我们可以修复的是我们的代码!

在Swift编译器中有一个隐藏的选项,它会打印出编译器编译每个函数所需的确切时间间隔:-Xfrontend -debug-time-function-bodies。它允许我们发现代码中的瓶颈,并显著改善编译时间。

在终端中简单运行如下命令并分析结果:

xcodebuild -workspace App.xcworkspace -scheme App clean build OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-function-bodies" | grep [1-9].[0-9]ms | sort -nr > culprits.txt

Awesome Brian Irace写了一篇关于它的精彩文章,分析你的Swift编译时间。

在我的情况下,Xcode 7没有任何不同。我有多个函数需要几秒钟来编译。

例子

// Build time: 5238.3ms
return CGSize(width: size.width + (rightView?.bounds.width ?? 0) + (leftView?.bounds.width ?? 0) + 22, height: bounds.height)

在打开可选项之后,构建时间下降了99.4%。

// Build time: 32.4ms
var padding: CGFloat = 22
if let rightView = rightView {
    padding += rightView.bounds.width
}

if let leftView = leftView {
    padding += leftView.bounds.width
}
return CGSizeMake(size.width + padding, bounds.height)

在这篇文章和这篇文章中可以看到更多的例子。

Xcode的构建时间分析器

我开发了一个Xcode插件,对于遇到这些问题的人来说可能会派上用场。

Swift 3似乎会有改进,所以希望我们能看到我们的Swift代码编译得更快。

重新启动我的Mac对这个问题有奇效。通过重新启动,我从15分钟构建到30秒构建。

如果您试图识别降低编译时间的特定文件,您可以尝试通过xctool从命令行编译它,它将逐文件提供编译时间。

需要注意的是,默认情况下,它会为每个CPU内核并发构建2个文件,并且不会提供“净”运行时间,而是绝对的“用户”时间。这样,并行文件之间的所有计时都很均匀,看起来非常相似。

为了克服这个问题,将-jobs标志设置为1,这样它就不会并行化文件构建。这将花费更长的时间,但最终您将拥有“净”编译时间,您可以逐个文件进行比较。

这是一个示例命令,应该可以做到这一点:

Xctool -workspace <your_workspace> -scheme <your_scheme> -jobs 1 build

“Compile Swift files”阶段的输出如下所示:

...
   ✓ Compile EntityObserver.swift (1623 ms)
   ✓ Compile Session.swift (1526 ms)
   ✓ Compile SearchComposer.swift (1556 ms)
...

从这个输出中,您可以快速确定哪些文件的编译时间比其他文件长。此外,您可以高精度地确定重构(显式强制转换、类型提示等)是否降低了特定文件的编译时间。

注意:从技术上讲,您也可以使用xcodebuild来实现,但输出非常冗长且难以使用。

事实证明罗伯·纳皮尔是对的。这是一个文件(实际上是一个方法),导致编译器去berzek。

别误会我的意思。Swift每次都会重新编译你所有的文件,但现在最棒的事情是,苹果在它编译的文件上添加了实时编译反馈,所以Xcode 6 GM现在可以实时显示哪些Swift文件正在编译以及编译状态,如下图所示:

因此,这是非常方便的,以了解您的文件花费这么长时间。在我的例子中,它是这段代码:

var dic = super.json().mutableCopy() as NSMutableDictionary
dic.addEntriesFromDictionary([
        "url" : self.url?.absoluteString ?? "",
        "title" : self.title ?? ""
        ])

return dic.copy() as NSDictionary

因为属性标题类型是var title:String?而不是NSString。当把它添加到NSMutableDictionary时,编译器要疯了。

改为:

var dic = super.json().mutableCopy() as NSMutableDictionary
dic.addEntriesFromDictionary([
        "url" : self.url?.absoluteString ?? "",
        "title" : NSString(string: self.title ?? "")
        ])

return dic.copy() as NSDictionary

使编译从10/15秒(甚至更多)下降到一秒…很神奇的。