XCode 构建系统:弄乱预处理器定义和包含的 header 文件?

XCode Build System: Messing up preprocessors definitions and included header files?

这里是第一个问题。

我在使用 XCode 构建系统时遇到了一些问题,特别是在预处理器定义方面。

我正在尝试为 objective-c 运行时定义一个宏,以避免强制将调度函数强制转换为适当的函数指针类型。通常的做法是使用 #define OBJC_OLD_DISPATCH_PROTOTYPES,然后在下一行包含 header。一旦 header 被包含,宏就已经被定义并且 header 被相应地配置。

但这就是它开始变得奇怪的地方!

宏根本无法识别,header 被包含,就好像 #define 语句不存在一样,所以它无法 #define OBJC_OLD_DISPATCH_PROTOTYPES 并且它被(重新?)定义作为 0.

main.c

#include <stdio.h>
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
#include <objc/objc-runtime.h>

int main(int argc, const char * argv[]) {
    // From there:
    //  - Build System: OBJC_OLD_DISPATCH_PROTOTYPES is always 0, except if defined in build settings
    //  - Clang (only): OBJC_OLD_DISPATCH_PROTOTYPES is 1
    printf("%d\n", OBJC_OLD_DISPATCH_PROTOTYPES);
}

当在“Apple Clang - 预处理”部分下的项目构建设置中定义预处理器宏时,构建系统将按预期运行。它使用 clang-D 参数定义全局宏,使其可用于项目使用的任何文件。

但是,当我使用 clang main.c.

从终端使用 clang 时,源代码编译正确

有人能告诉我构建系统正常运行需要配置什么吗?

使用 Xcode IDE:

构建时会发出警告
Ambiguous expansion of macro 'OBJC_OLD_DISPATCH_PROTOTYPES'

并且直接使用 Xcode 输出确实是 0,但是使用 clang main.c 输出是 1。不同之处在于 Xcode 默认情况下将 clang 与已启用的模块一起使用:如果您在此处启用模块,则会在命令行上收到相同的警告:

clang -fmodules main.c  

解决方案

在 Xcode、select 目标中,转到“构建设置”选项卡,然后在“Apple Clang - 语言 - 模块”部分,切换“启用模块(C 和 Objective-C)" 进入 'NO':

那么无论您在命令行中使用 Xcode 还是 Clang,在这两种情况下您都会得到预期的结果。

解释:

如果您使用模块,会发生以下情况:

  • 使用模块的二进制表示代替包括文本和编译结果的预处理器
  • 模块是(独立地)预编译的,即它们使用模块预编译时的定义
  • 因此,include/import 语句之前代码中的预处理定义对模块(或其他导入的模块)没有影响。
  • 如果启用了模块,不仅@imports 会受到影响,#includes 也会在后台转换为模块导入

所以你对 OBJC_OLD_DISPATCH_PROTOTYPES 的定义是矛盾的。 预编译模块使用 0 作为 OBJC_OLD_DISPATCH_PROTOTYPES,您将其重新定义为 1.

顺便说一句:如果你使用

#define OBJC_OLD_DISPATCH_PROTOTYPES 0

然后您使用预编译模块正在使用的相同定义,因此即使启用了模块,也不会出现有关宏扩展不明确的警告。

没有启用的模块,预处理器包括文本,编译结果和returns预期结果,即在objc.h中使用所需的typedef。