这就是TLDR.对于整个故事,请继续阅读……
在(Obj)C中,使API可用于源代码文件的旧方法是文本包含.预处理器会看到你的#import< Foundation / Foundation.h>指令并将该头文件中的所有文本(以及它包含的任何其他头文件及其包含的头文件等)复制到源文件中,然后再将其传递给编译器.正如您所料,为项目中的每个文件重新编译数千行系统头声明并不是那么高效.
因此,我们在几年前获得了预编译头文件 – 您将常用的#imports放在一个位置,这些部件的编译步骤将完成一次,结果是编译器后端可以重用于项目中的每个文件.但这也存在问题:保持PCH满意是一种维护负担,并且它不允许您限制每个源文件中使用的命名空间(即,如果您希望项目中的.m文件仅查看符号它需要使用,而不是项目中其他地方使用的所有其他东西.
最重要的是,文本包含具有潜在的脆弱性问题.如果#define在#import行之上的某些内容,并且定义更改了导入标头中使用的符号,那么这些标头将出现编译错误(或者以更微妙的方式失败,例如定义错误的API).有些惯例可以防止这种情况发生,但是约定并没有强制执行 – 你总是一个错字/新的团队成员/糟糕的合并,远离一切分崩离析.
无论如何,即使使用预编译的头文件,文本包含也不是很好,所以在Xcode 5 Apple中引入了模块. (实际上,不仅仅是Apple.They’re in the LLVM/Clang compiler suite,so they’re open source.)模块基于语义导入,而不是文本包含 – 也就是说,模块在抽象层面告诉编译器它可以为源代码提供哪些API符号,而不是粘贴在文本中.这些符号的定义 – 因此它们不易碎,并且它们在后端单独预编译,因此构建项目的速度很快.
模块现在是ObjC项目的默认值. (请注意,如果您创建一个新的ObjC项目,它不再包含预编译的头.您可以关闭模块,所以如果您有一个旧项目,您可能仍然使用文本包含和预编译头.)您可以找到更多关于Session 404 from WWDC 2013的ObjC模块.
为什么关于ObjC的所有这些业务?我们正在谈论斯威夫特,对吧?好吧,Swift基于很多相同的基础设施.
Swift从一开始就使用模块,所以它总是基于语义导入.这意味着没有构建时间性能,也没有脆弱性. Swift导入所做的就是告诉编译器你需要什么符号(以及链接器在生成二进制可执行文件时在哪里找到它们).
因此,将相同的导入放在每个文件顶部的唯一成本是键入.这是一个必要的成本 – 在Swift中,源文件是一个语义单元,确定进入它的内容具有真正的意义.例如,如果您导入Foundation,许多Swift标准库类型的行为会发生变化,以启用Cocoa集合和值类型的桥接 – 如果您的应用程序的某个部分想要严格使用Swift集合和值类型,那么您可能会不想导入Foundation(或Cocoa或UIKit或包含它的其他东西).
更新:此外,您选择在Swift文件中导入的内容具有实际意义.
例如,编译器如何优化泛型和静态/动态分派取决于给定文件中可见的声明,因此如果导入的内容超出了您的需要,则可能会生成较慢的代码.所以一般来说,最好只导入你需要的东西.
明确的导入也有助于提高清晰度和可读性.如果导入是在整个项目范围内进行的,那么当您将代码复制粘贴到另一个项目中时,您会在新位置看到很多错误…而且您需要解决的导入数据要少得多他们.
“但我讨厌在每个档案的顶部放置相同的几个进口,”你说.让我们考虑一点.
>你真的需要几个吗?大多数模块都可以传递它们的依赖关系.如果您已经导入Cocoa(OS X)或UIKit(iOS / tvOS / watchOS),则无需导入Foundation.例如,如果您正在编写SpriteKit或SceneKit游戏,您将自动获得UIKit / Cocoa(适用于任何平台)和Foundation.
>你真的需要在每个文件中都一样吗?当然,你在UIKit项目中,所以你几乎到处都在使用UIKit.但这只是一个导入,顶部有12个字符.也许您的项目也使用了Contacts或Photos或CoreBluetooth或HealthKit ……但它可能不需要在您定义的每种类型中使用所有这些. (如果确实如此,您的代码可能会受到关注点分离不良的影响.)
>您是否真的一直在管理进口报表?我不知道你的项目,但在我参与的大多数大型项目中,我要说至少90%的开发活动涉及编辑现有的源文件,而不是创建新文件……在开始项目工作之后或者主要功能,我们很少(重新)定义源文件集或其依赖项.并且有一些快捷方式可以帮助(除其他外)设置导入,如Xcode file templates.