Swift 中是否有前缀 Header(或具有此功能的东西)?
Is there a Prefix Header (or something with this functionality) in Swift?
有没有办法在 Swift 中获得前缀 header 的功能?我不想在使用它们的每个文件中导入外部库。
没有。但您不需要它 — import UIKit
除了您输入 12 个字符所花费的时间外,没有任何成本。 (或者使用已经有它们的 Xcode 新文件模板。)
这就是 TLDR。对于整个故事,请继续阅读...
在 (Obj)C 中,使 API 可用于源代码文件的旧方法是 文本包含 。预处理器会看到你的 #import <Foundation/Foundation.h>
指令并 从 header 文件(以及它包含的任何其他 header 文件中复制所有文本 ,并且headers they include, 依此类推) 到您的源文件中,然后再将其传递给编译器。如您所料,为项目 中的每个文件重新编译数千行系统header 声明并不是那么高效。
所以,几年前我们得到了预编译的 headers——你把你常用的 #import
s 放在一个地方,这些部分的编译步骤将完成一次,用结果编译器后端可以重用项目中的每个文件。但这也有它的问题:让你的 PCH 快乐有一个维护负担,并且它不允许你限制每个源文件中使用的命名空间(即如果你想在你的项目中看到一个 .m
文件只有它需要使用的符号,而不是项目中其他地方使用的所有其他东西)。
最重要的是,文本包含有一个潜在的脆弱性问题。如果你 #define
在你的 #import
行之上的东西,并且定义更改了导入的 headers 中使用的符号,那些 headers 将有编译错误(或失败更多微妙的方式,比如定义错误 API)。有一些惯例可以防止这种情况发生,但惯例并没有被强制执行——你总是一个打字错误/新团队成员/糟糕的合并远离一切分崩离析。
无论如何,即使使用预编译的 header,文本包含也不是很好,因此在 Xcode 5 中,Apple 引入了 模块 。 (实际上,不仅仅是 Apple。They're in the LLVM/Clang compiler suite, so they're open source.)模块基于语义导入,而不是文本包含——也就是说,模块在抽象层面上告诉编译器什么API 它使您的源代码可用的符号,而不是粘贴这些符号定义的文本 — 因此它们不易损坏,并且它们在后端单独预编译,因此构建您的项目保持快速。
模块现在是 ObjC 项目的默认设置。 (请注意,如果您创建一个新的 ObjC 项目,它不再包含预编译的 header。您可以关闭模块,因此如果您有一个旧项目,您可能仍在使用文本包含和预编译的 headers。)您可以在 Session 404 from WWDC 2013.
中找到有关 ObjC 模块的更多信息
为什么所有关于 ObjC 的业务?我们正在谈论 Swift,对吗?好吧,Swift 基于很多相同的基础设施。
Swift 从一开始就使用模块,所以它总是基于语义导入。这意味着没有 build-time 性能下降,也没有脆弱性。 Swift import
所做的就是告诉编译器您需要什么符号(以及链接器在生成二进制可执行文件时在哪里可以找到它们)。
因此,将相同的 import
放在每个文件的顶部的唯一成本就是输入。这是必要的成本 — 在 Swift 中,源文件是一个语义单元,决定其中包含的内容具有实际意义。例如,如果你 import Foundation
,许多 Swift 标准库类型的行为会改变,以启用与 Cocoa collection 和值类型的桥接——如果你的一部分想要严格使用 Swift collection 和值类型的应用程序,您可能不想导入 Foundation(或 Cocoa 或 UIKit 或其他包含它的东西)。
更新: 此外,您在 Swift 文件中选择 import
具有实际意义。
例如,编译器如何优化泛型和 static/dynamic 分派取决于给定文件中可见的声明,因此如果您 import
超出需要,您可能会生成较慢的代码.所以一般来说,最好只import
你需要的。
显式导入也有助于提高清晰度和可读性。如果 import
是 project-wide,那么当您 copy-paste 从一个项目编码到另一个项目时,您会在新位置看到很多错误...不太清楚你需要什么 import
来解决它们。
“但我讨厌一直将相同的几个 import
放在每个文件的顶部,”你说。让我们考虑一下。
你真的需要几个吗?大多数模块传递 import
它们的依赖项。如果您已经 import
ing Cocoa (OS X) 或 UIKit (iOS/tvOS/watchOS),则不需要 import Foundation
。例如,如果您正在编写 SpriteKit 或 SceneKit 游戏,您会自动免费获得 UIKit/Cocoa(适用于任何平台)和 Foundation。
您真的需要在每个文件中使用相同的吗?当然,你在一个 UIKit 项目中,所以你几乎到处都在使用 UIKit。但这只是一个 import
,顶部有十二个字符。也许你的项目也在使用联系人或照片或 CoreBluetooth 或 HealthKit ......但它可能不需要在你定义的每种类型中使用所有这些。 (如果是这样,您的代码可能存在关注点分离不佳的问题。)
您真的一直在管理 import
语句 吗?我不知道你的项目,但在我从事的大多数大型项目中,我想说至少 90% 的开发 activity 涉及编辑现有的源文件,而不是创建新的...之后在开始处理项目或主要功能时,我们很少(重新)定义源文件集或其依赖项。还有一些快捷方式可以帮助(除其他外)设置导入,例如 Xcode file templates.
创建一个Objective-C桥接Header文件:
[New File→iOS→Source→Header File]: Bridging-Header.h
转到这个新的 header 并导入您的外部库:
@import Module1Name;
@import Module2Name;
...
进入Build Settings,设置Objective-C Bridging的路径Header :
[Target→Build Settings→Swift Compiler - Code Generation→Objective-C Bridging Header]: $(SRCROOT)/.../Bridging-Header.h
然后您可以在每个文件中使用外部库而无需 import
代码。
参考文献:
There 是一个 -enable-bridging-pch
特征。但似乎在 Xcode 9 中不起作用 :(
我决定写这篇回答只是为了完全涵盖这个话题。
您可以创建模块来导入您的依赖项并只导入它。
例如调用Core
。它将只包含单个 swift 导入文件。
每次导入都应以 @_exported
关键字开头。
例如:
@_exported import UIKit;
@_exported import Combine;
有没有办法在 Swift 中获得前缀 header 的功能?我不想在使用它们的每个文件中导入外部库。
没有。但您不需要它 — import UIKit
除了您输入 12 个字符所花费的时间外,没有任何成本。 (或者使用已经有它们的 Xcode 新文件模板。)
这就是 TLDR。对于整个故事,请继续阅读...
在 (Obj)C 中,使 API 可用于源代码文件的旧方法是 文本包含 。预处理器会看到你的 #import <Foundation/Foundation.h>
指令并 从 header 文件(以及它包含的任何其他 header 文件中复制所有文本 ,并且headers they include, 依此类推) 到您的源文件中,然后再将其传递给编译器。如您所料,为项目 中的每个文件重新编译数千行系统header 声明并不是那么高效。
所以,几年前我们得到了预编译的 headers——你把你常用的 #import
s 放在一个地方,这些部分的编译步骤将完成一次,用结果编译器后端可以重用项目中的每个文件。但这也有它的问题:让你的 PCH 快乐有一个维护负担,并且它不允许你限制每个源文件中使用的命名空间(即如果你想在你的项目中看到一个 .m
文件只有它需要使用的符号,而不是项目中其他地方使用的所有其他东西)。
最重要的是,文本包含有一个潜在的脆弱性问题。如果你 #define
在你的 #import
行之上的东西,并且定义更改了导入的 headers 中使用的符号,那些 headers 将有编译错误(或失败更多微妙的方式,比如定义错误 API)。有一些惯例可以防止这种情况发生,但惯例并没有被强制执行——你总是一个打字错误/新团队成员/糟糕的合并远离一切分崩离析。
无论如何,即使使用预编译的 header,文本包含也不是很好,因此在 Xcode 5 中,Apple 引入了 模块 。 (实际上,不仅仅是 Apple。They're in the LLVM/Clang compiler suite, so they're open source.)模块基于语义导入,而不是文本包含——也就是说,模块在抽象层面上告诉编译器什么API 它使您的源代码可用的符号,而不是粘贴这些符号定义的文本 — 因此它们不易损坏,并且它们在后端单独预编译,因此构建您的项目保持快速。
模块现在是 ObjC 项目的默认设置。 (请注意,如果您创建一个新的 ObjC 项目,它不再包含预编译的 header。您可以关闭模块,因此如果您有一个旧项目,您可能仍在使用文本包含和预编译的 headers。)您可以在 Session 404 from WWDC 2013.
中找到有关 ObjC 模块的更多信息为什么所有关于 ObjC 的业务?我们正在谈论 Swift,对吗?好吧,Swift 基于很多相同的基础设施。
Swift 从一开始就使用模块,所以它总是基于语义导入。这意味着没有 build-time 性能下降,也没有脆弱性。 Swift import
所做的就是告诉编译器您需要什么符号(以及链接器在生成二进制可执行文件时在哪里可以找到它们)。
因此,将相同的 import
放在每个文件的顶部的唯一成本就是输入。这是必要的成本 — 在 Swift 中,源文件是一个语义单元,决定其中包含的内容具有实际意义。例如,如果你 import Foundation
,许多 Swift 标准库类型的行为会改变,以启用与 Cocoa collection 和值类型的桥接——如果你的一部分想要严格使用 Swift collection 和值类型的应用程序,您可能不想导入 Foundation(或 Cocoa 或 UIKit 或其他包含它的东西)。
更新: 此外,您在 Swift 文件中选择 import
具有实际意义。
例如,编译器如何优化泛型和 static/dynamic 分派取决于给定文件中可见的声明,因此如果您 import
超出需要,您可能会生成较慢的代码.所以一般来说,最好只import
你需要的。
显式导入也有助于提高清晰度和可读性。如果 import
是 project-wide,那么当您 copy-paste 从一个项目编码到另一个项目时,您会在新位置看到很多错误...不太清楚你需要什么 import
来解决它们。
“但我讨厌一直将相同的几个 import
放在每个文件的顶部,”你说。让我们考虑一下。
你真的需要几个吗?大多数模块传递
import
它们的依赖项。如果您已经import
ing Cocoa (OS X) 或 UIKit (iOS/tvOS/watchOS),则不需要import Foundation
。例如,如果您正在编写 SpriteKit 或 SceneKit 游戏,您会自动免费获得 UIKit/Cocoa(适用于任何平台)和 Foundation。您真的需要在每个文件中使用相同的吗?当然,你在一个 UIKit 项目中,所以你几乎到处都在使用 UIKit。但这只是一个
import
,顶部有十二个字符。也许你的项目也在使用联系人或照片或 CoreBluetooth 或 HealthKit ......但它可能不需要在你定义的每种类型中使用所有这些。 (如果是这样,您的代码可能存在关注点分离不佳的问题。)您真的一直在管理
import
语句 吗?我不知道你的项目,但在我从事的大多数大型项目中,我想说至少 90% 的开发 activity 涉及编辑现有的源文件,而不是创建新的...之后在开始处理项目或主要功能时,我们很少(重新)定义源文件集或其依赖项。还有一些快捷方式可以帮助(除其他外)设置导入,例如 Xcode file templates.
创建一个Objective-C桥接Header文件:
[New File→iOS→Source→Header File]:
Bridging-Header.h
转到这个新的 header 并导入您的外部库:
@import Module1Name; @import Module2Name; ...
进入Build Settings,设置Objective-C Bridging的路径Header :
[Target→Build Settings→Swift Compiler - Code Generation→Objective-C Bridging Header]:
$(SRCROOT)/.../Bridging-Header.h
然后您可以在每个文件中使用外部库而无需 import
代码。
参考文献:
There 是一个 -enable-bridging-pch
特征。但似乎在 Xcode 9 中不起作用 :(
我决定写这篇回答只是为了完全涵盖这个话题。
您可以创建模块来导入您的依赖项并只导入它。
例如调用Core
。它将只包含单个 swift 导入文件。
每次导入都应以 @_exported
关键字开头。
例如:
@_exported import UIKit;
@_exported import Combine;