如何处理大型 Swift 项目?
How to handle large Swift Project?
我在 Swift 中编写的 iPhone 应用变得相当大(> 150 .swift 文件 + 各种 Objective-C 库),Xcode 开始表现得很糟糕:
每次编译我都会遇到各种错误,例如:
Command failed due to signal: Segmentation fault: 11
- 编译需要大量时间(在 MacBook Pro Retina 上 > 2 分钟)
- 等等。
我只是想知道是否每个人都有同样的问题,也许有人找到了减少这种噩梦的方法?
到目前为止我所做的 — 我将项目分成几个动态框架,我 link 来自主项目,它有助于减少编译时间,但引入了一些新问题。
我还使用 iRamDisk 将 DerivedData
文件夹保存在 RAM 中并定期删除其中的所有文件,它有时有助于解决 SourceKit 崩溃问题。
你可以试试:
- 升级计算机中的 RAM 容量
- 如果您有多个 .swift 文件在同一个视图控制器上执行操作,请尝试将它们压缩为每个视图控制器一个 .swift 文件
- 调整编译源下的设置以查看是否有任何重复项,或者是否有任何脚本或设置可以添加以使其编译更快...
您还可以查看 this post 的答案,以获取有关如何减慢编译时间的一些提示
Apple 有一些建议可以加快 Technical Note 2190. Have you thought about creating and precompiling an own framework 中的 Xcode 构建,用于外包未更改的 Swift 模块或 some/all Objective-C 代码?
删除 Swift 中的所有类型推断。
This SO topic has some nice ideas and this blog post 建议
- 停止生成 dSYM 包并且
- 如果使用 Clang,请避免使用
-O4
进行编译。
尽管这些改进中有很多与 Objective-C 有关,但我很确定,其中一些改进仍然与 Swift 有关。
(重新)编译是一个已知问题,我相信很快就会得到解决。一些建议:
- 尽可能使用 Objective C - 即使它是 Swift 项目的一部分,编译速度也很快
- 将代码拆分为框架
- 指定类型而不是让编译器来推断它们
同样,这个问题很可能会很快得到修复,所以此时最好不要在重写或重组代码方面投入大量资金。
Swift 工具链仍然有点粗糙,您需要使用一些临时解决方法,直到 Apple 修复它(请参阅下面的 UPDATES)
这里列出了您可以采取的一些措施,以防止自己发疯。
不成熟的 Swift 编译器
导致速度缓慢
使用 Injection for Xcode 更改您的开发工作流程。安装插件后,您将能够在 simulator\device 中注入代码更改,而无需 重新编译。您不需要 hardcode\modify 项目中的任何内容。我们最近开始在工作中使用它,它对我们产生了巨大的影响,即使它并不适用于每个用例(例如,您不能创建新功能,只能修改现有功能)。
编译器不喜欢某些特定的代码结构,并且需要花费太多时间进行编译。最常见的问题是类型检查器会根据需要执行的类型检查次数以指数方式减慢编译时间(阅读更多内容 here for practical examples and here for a detailed explanation). In order to identify if you are suffering from this problem you can follow this blog post, you will gather information about the functions that creates slowness by using some compiler additional flags. Alternatively you can use this Xcode plugin 以确定构建缓慢的根源。
在有意义的地方明智地使用动态框架。仅当您修改其 Swift 文件之一时才会完成框架重新编译(动态框架仅适用于 iOS >= 7)。
在同一个文件中压缩代码。减少 Swift 文件的数量可以显着加快编译过程。您可以通过添加用户定义的自定义标志 SWIFT_WHOLE_MODULE_OPTIMIZATION 并将其设置为 YES 并同时将优化级别设置为 [= 来轻松实现 "Whole module optimization" 110=](禁用会使它变慢的优化)OUTDATED 你可以考虑使用这个 gist,它是一个构建脚本,可以折叠你的所有代码在 "merge.swift" 文件中。
你需要为它创建一个新的目标,但它值得
尝试。
仔细检查列出的内容here(还有一些其他原因,因为编译速度很慢)
OUTDATED 尝试此 blog post 中描述的方法,它涉及创建生成 make 文件的构建脚本。它需要对构建脚本进行手动干预(它包含 swift 文件列表)。
OUTDATED 尝试 this 破解增量编译技术
更新:在 Swift 1.2 (Xcode 6.3)
上引入增量构建
Apple 终于在 Swift 1.2 中引入了增量构建(随 Xcode 6.3 一起提供)。它仍然不完美,但它是一个巨大的改进。
从现在开始,只有当 class 被更改时(或者它所依赖的 class 之一被更改时)才会重新编译。
然而,编译器仍然无法理解对 class 的更改是否是对其接口的更改。因此,对 class 的任何类型的更改都会导致重新编译 class 及其所有依赖项。
更新:仅当在 Swift 2.1 (Xcode 7.1) 上引入的 public 接口更改时,才重新编译相关的 classes
从 Swift 2.1 (Xcode 7.1) 开始,仅当您更改 class 的 public 接口时,依赖的 classes 才会重新编译,而不是每次更改。这会产生巨大的差异,特别是对于大型项目。
项目(错误)配置(与 Swift 无关)
- 确保 "Build Active Architecture Only" 是调试。
- 确保您没有添加 pre\post 花费太多时间的编译脚本。
我发现分段错误和编译缓慢的主要原因之一是对大数组和字典进行硬编码,尤其是在将它们声明为全局常量并尝试从另一个 .swift 文件。当我将所有数据存储在 plist 中时,这些问题就消失了。
根据我的经验,避免创建大型 swift
文件,当我在我的新公司开始一个项目时,那里是一个超过 2000 行的 'UIViewController',这个文件的小改动花费了很多时间来构建,我用 <500 行 class
做了 4 extension
s,我的速度提升令人难以置信.
我在 Swift 中编写的 iPhone 应用变得相当大(> 150 .swift 文件 + 各种 Objective-C 库),Xcode 开始表现得很糟糕:
每次编译我都会遇到各种错误,例如:
Command failed due to signal: Segmentation fault: 11
- 编译需要大量时间(在 MacBook Pro Retina 上 > 2 分钟)
- 等等。
我只是想知道是否每个人都有同样的问题,也许有人找到了减少这种噩梦的方法?
到目前为止我所做的 — 我将项目分成几个动态框架,我 link 来自主项目,它有助于减少编译时间,但引入了一些新问题。
我还使用 iRamDisk 将 DerivedData
文件夹保存在 RAM 中并定期删除其中的所有文件,它有时有助于解决 SourceKit 崩溃问题。
你可以试试:
- 升级计算机中的 RAM 容量
- 如果您有多个 .swift 文件在同一个视图控制器上执行操作,请尝试将它们压缩为每个视图控制器一个 .swift 文件
- 调整编译源下的设置以查看是否有任何重复项,或者是否有任何脚本或设置可以添加以使其编译更快...
您还可以查看 this post 的答案,以获取有关如何减慢编译时间的一些提示
Apple 有一些建议可以加快 Technical Note 2190. Have you thought about creating and precompiling an own framework 中的 Xcode 构建,用于外包未更改的 Swift 模块或 some/all Objective-C 代码?
删除 Swift 中的所有类型推断。
This SO topic has some nice ideas and this blog post 建议
- 停止生成 dSYM 包并且
- 如果使用 Clang,请避免使用
-O4
进行编译。
尽管这些改进中有很多与 Objective-C 有关,但我很确定,其中一些改进仍然与 Swift 有关。
(重新)编译是一个已知问题,我相信很快就会得到解决。一些建议:
- 尽可能使用 Objective C - 即使它是 Swift 项目的一部分,编译速度也很快
- 将代码拆分为框架
- 指定类型而不是让编译器来推断它们
同样,这个问题很可能会很快得到修复,所以此时最好不要在重写或重组代码方面投入大量资金。
Swift 工具链仍然有点粗糙,您需要使用一些临时解决方法,直到 Apple 修复它(请参阅下面的 UPDATES)
这里列出了您可以采取的一些措施,以防止自己发疯。
不成熟的 Swift 编译器
导致速度缓慢使用 Injection for Xcode 更改您的开发工作流程。安装插件后,您将能够在 simulator\device 中注入代码更改,而无需 重新编译。您不需要 hardcode\modify 项目中的任何内容。我们最近开始在工作中使用它,它对我们产生了巨大的影响,即使它并不适用于每个用例(例如,您不能创建新功能,只能修改现有功能)。
编译器不喜欢某些特定的代码结构,并且需要花费太多时间进行编译。最常见的问题是类型检查器会根据需要执行的类型检查次数以指数方式减慢编译时间(阅读更多内容 here for practical examples and here for a detailed explanation). In order to identify if you are suffering from this problem you can follow this blog post, you will gather information about the functions that creates slowness by using some compiler additional flags. Alternatively you can use this Xcode plugin 以确定构建缓慢的根源。
在有意义的地方明智地使用动态框架。仅当您修改其 Swift 文件之一时才会完成框架重新编译(动态框架仅适用于 iOS >= 7)。
在同一个文件中压缩代码。减少 Swift 文件的数量可以显着加快编译过程。您可以通过添加用户定义的自定义标志 SWIFT_WHOLE_MODULE_OPTIMIZATION 并将其设置为 YES 并同时将优化级别设置为 [= 来轻松实现 "Whole module optimization" 110=](禁用会使它变慢的优化)OUTDATED
你可以考虑使用这个 gist,它是一个构建脚本,可以折叠你的所有代码在 "merge.swift" 文件中。 你需要为它创建一个新的目标,但它值得 尝试。仔细检查列出的内容here(还有一些其他原因,因为编译速度很慢)
OUTDATED
尝试此 blog post 中描述的方法,它涉及创建生成 make 文件的构建脚本。它需要对构建脚本进行手动干预(它包含 swift 文件列表)。OUTDATED
尝试 this 破解增量编译技术
更新:在 Swift 1.2 (Xcode 6.3)
上引入增量构建Apple 终于在 Swift 1.2 中引入了增量构建(随 Xcode 6.3 一起提供)。它仍然不完美,但它是一个巨大的改进。
从现在开始,只有当 class 被更改时(或者它所依赖的 class 之一被更改时)才会重新编译。 然而,编译器仍然无法理解对 class 的更改是否是对其接口的更改。因此,对 class 的任何类型的更改都会导致重新编译 class 及其所有依赖项。
更新:仅当在 Swift 2.1 (Xcode 7.1) 上引入的 public 接口更改时,才重新编译相关的 classes
从 Swift 2.1 (Xcode 7.1) 开始,仅当您更改 class 的 public 接口时,依赖的 classes 才会重新编译,而不是每次更改。这会产生巨大的差异,特别是对于大型项目。
项目(错误)配置(与 Swift 无关)
- 确保 "Build Active Architecture Only" 是调试。
- 确保您没有添加 pre\post 花费太多时间的编译脚本。
我发现分段错误和编译缓慢的主要原因之一是对大数组和字典进行硬编码,尤其是在将它们声明为全局常量并尝试从另一个 .swift 文件。当我将所有数据存储在 plist 中时,这些问题就消失了。
根据我的经验,避免创建大型 swift
文件,当我在我的新公司开始一个项目时,那里是一个超过 2000 行的 'UIViewController',这个文件的小改动花费了很多时间来构建,我用 <500 行 class
做了 4 extension
s,我的速度提升令人难以置信.