Swift中是否有前缀标题(或具有此功能的东西)?

stk*_*stk 7 import header prefix swift

有没有办法在Swift中获得前缀头的功能?我不想在使用它们的每个文件中导入外部库.

ric*_*ter 32

不,但你不需要它 - import UIKit超过你输入12个字符的时间是没有成本的.(或者使用已经存在它们的Xcode新文件模板.)

这是TLDR.对于整个故事,请继续阅读......


在(Obj)C中,使API可用于源代码文件的旧方法是文本包含.预处理器会看到你的#import <Foundation/Foundation.h>指令并将该头文件中的所有文本(以及它包含的任何其他头文件,以及它们包含的头文件等等)复制到源文件中,然后再将其传递给编译器.正如您所料,为项目中的每个文件重新编译数千行系统头声明并不是那么高效.

因此,我们在几年前获得了预编译头文件 - 您将常用文件#import放在一个位置,这些部件的编译步骤将完成一次,结果是编译器后端可以重用于项目中的每个文件.但这也存在问题:保持PCH满意是一种维护负担,并且它不允许您限制每个源文件中使用的命名空间(即,如果您希望.m项目中的一个文件只看到它需要的符号使用,而不是项目中其他地方使用的所有其他东西).

最重要的是,文本包含具有潜在的脆弱性问题.如果你#define#import行以上的东西,并且定义更改了导入标题中使用的符号,那些标题将有编译错误(或以更微妙的方式失败,如定义错误的API).有些惯例可以防止这种情况发生,但是约定并没有强制执行 - 你总是一个错字/新的团队成员/糟糕的合并,远离一切分崩离析.

无论如何,即使使用预编译的头文件,文本包含也不是很好,所以在Xcode 5 Apple中引入了模块.(实际上,不只是Apple.他们在LLVM/Clang编译器套件中,因此它们是开源的.)模块基于语义导入,而不是文本包含 - 也就是说,模块在抽象层面告诉编译器什么它使源代码可用的API符号,而不是粘贴在这些符号的定义文本中 - 因此它们不易碎,并且它们在后端单独预编译,因此构建项目的速度很快.

模块现在是ObjC项目的默认模块.(请注意,如果您创建一个新的ObjC项目,它不再包含预编译的头.您可以关闭模​​块,所以如果您有一个旧项目,您可能仍然使用文本包含和预编译头.)您可以找到更多关于WWDC 2013会话404中的 ObjC模块.


为什么关于ObjC的所有这些业务?我们正在谈论斯威夫特,对吧?好吧,Swift基于很多相同的基础设施.

Swift从一开始就使用模块,所以它始终基于语义导入.这意味着没有构建时间性能,也没有脆弱性.Swift import所做的就是告诉编译器你需要什么符号(以及链接器在生成二进制可执行文件时在哪里找到它们).

因此,将相同的imports放在每个文件顶部的唯一成本是打字.这是一个必要的成本 - 在Swift中,源文件是一个语义单元,确定进入它的内容具有真正的意义.例如,如果您import Foundation要启用与Cocoa集合和值类型的桥接,许多Swift标准库类型的行为都会发生变化- 如果您的应用程序的某个部分想要严格使用Swift集合和值类型,您可能不需要导入Foundation(或Cocoa或UIKit或包含它的其他东西).


更新:此外,您import在Swift文件中选择的内容具有实际意义.

例如,编译器如何优化泛型和静态/动态分派取决于给定文件中可见的声明,因此如果import您需要的更多,则可能会生成更慢的代码.所以一般来说,最好import只满足你的需求.

显式导入还有助于提高清晰度和可读性.如果imports是项目范围的,那么当你将代码从一个项目复制粘贴到另一个项目中时,你会在新位置看到很多错误...而且import你需要解决的问题要清楚得多他们.


"但我讨厌把import所有的文件放在每个文件的顶部,"你说.让我们考虑一下.

  • 你真的需要几个吗?大多数模块都是传递import它们的依赖关系.import Foundation如果你已经在使用importCocoa(OS X)或UIKit(iOS/tvOS/watchOS),则不需要.例如,如果您正在编写SpriteKit或SceneKit游戏,您将自动获得UIKit/Cocoa(适用于任何平台)和Foundation.

  • 你真的需要在每个档案中都一样吗?当然,你在UIKit项目中,所以你几乎到处都在使用UIKit.但这只是import顶部的十二个字符.也许您的项目也使用了Contacts或Photos或CoreBluetooth或HealthKit ......但它可能不需要在您定义的每种类型中使用所有这些.(如果确实如此,您的代码可能会受到关注点分离不良的影响.)

  • 你真的管理import报表所有的时间?我不知道你的项目,但在我参与的大多数大型项目中,我认为至少90%的开发活动涉及编辑现有的源文件,而不是创建新文件......在开始项目工作之后或者主要功能,我们很少(重新)定义源文件集或其依赖项.还有一些快捷方式可以帮助(除其他外)设置导入,比如Xcode文件模板.

  • 我同意你的观点,有些情况虽然*在所有源文件中导入一些新东西*不是代码味道.例如,将日志记录功能添加到项目中需要将其导入到需要它的每个源文件中.遗憾的是,没有一种机制可以在整个项目中获得日志记录功能. (3认同)
  • +1 为优秀的答案!但对我来说,每次在每个源文件中导入我需要的所有东西似乎仍然“不正确”。它有一些代码重复的味道,即使它可能不完全是那样。如果我需要在 _all_ 源文件中导入一些新东西怎么办?我是否需要打开所有 485 个并添加新的导入行?这不可能是最终的解决方案...... (2认同)
  • 如果你有一个项目,485源文件确实每个都有*完全相同的*依赖集,可能有代码味道,但不是语言...**over*import也没有多少成本,但是那里每个文件只导入它使用的内容也是很好的理由. (2认同)

jqg*_*imo 19

  1. 创建Objective-C桥接头文件:

    [新文件→iOS→源→头文件]: Bridging-Header.h

  2. 转到此新标头并导入外部库:

    @import Module1Name;
    @import Module2Name;
    ...
    
    Run Code Online (Sandbox Code Playgroud)
  3. 转到Build Settings,设置Objective-C Bridging Header的路径:

    [目标→构建设置→Swift编译器 - 代码生成→Objective-C桥接头]: $(SRCROOT)/.../Bridging-Header.h

然后,您可以在没有import代码的每个文件中使用外部库.


参考文献: