假设我有一个Sockets框架,它定义了一个Obj-C协议SocketResponder。然后假设在我的服务框架中,它依赖于Sockets框架并与之链接。我的服务框架混合了Obj-C和少量Swift代码,其中一个Obj-C类型,假设NetworkTimeResponder符合SocketResponder协议,因此:
// Services/NetworkTime.h
#import <Sockets/Sockets.h>
@interface NetworkTimeResponder: <SocketResponder>
…
这看起来非常简单,但是编译器给出了错误:第一个月
我读过很多关于这个问题的“解决方案”(StackOverflow结果的每一页),但没有一个适用于这个场景。
我相信我理解这个问题,如果应用程序然后导入服务和套接字框架的头部,那么套接字框架头部中实际上有两个类型定义,因此可能会有问题。
但我真的很困惑,因为这与AppKit导入Foundation并声明符合NSCopying
的类型,以及我的应用程序同时导入AppKit和Foundation有什么不同?这个问题似乎只出现在 modules 的情况下,这是因为我的一个框架中有一点Swift?我不熟悉模块的根本区别和影响。
有人能解释一下吗?
1条答案
按热度按时间l5tcr1uw1#
TLDR:导入和链接的目标框架也需要是一个模块。
我正在使用的这些项目都是十年前启动的,所以它们的编译器设置也随着时间的推移而变化。“定义模块”和“启用C和Obj-C模块”(
DEFINES_MODULE
和CLANG_ENABLE_MODULES
)都是“关闭”的。然而,在我的一个框架中添加了一点Swift代码后,Xcode将DEFINES_MODULE
和CLANG_ENABLE_MODULES
都设置为“是”。现在,如果“模块化”框架 * 在其任何公共头文件内 * 从“非模块化”框架导入任何公共头文件,就会弹出此错误。
它真正归结为“嘿,这个目标是一个模块,而那个不是”。
现在,为什么这是一个问题?这部分地探究了为什么模块首先存在。如果我们看看
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES
(一些不太了解的人会建议打开它),那么答案就变得更清楚了:“启用此设置允许从框架模块内使用非模块化包含。这本身是不安全的,因为当导入框架和非模块化包含的任何客户端使用此类标头时,可能会导致重复定义。”
如果你知道header include是如何工作的,并且你理解了为什么C/C++在头文件中使用大量的#ifdef保护来避免重复声明,那么关键是:模块避免了所有这些麻烦,并且保证只有一个声明存在。通过“允许框架模块中的非模块包含”,现在你重新引入了重复的可能性。
https://clang.llvm.org/docs/Modules.html
所以这个故事的寓意是,如果一个给定的目标是一个模块,那么它#在公共头中导入的所有东西也应该是一个模块。