首页 > 解决方案 > XCode 构建系统:弄乱预处理器定义和包含的头文件?

问题描述

这里的第一个问题。

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

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

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

该宏根本无法识别,并且标头被包含,就好像该#define语句不存在一样,因此它失败#define OBJC_OLD_DISPATCH_PROTOTYPES并且它被(重新?)定义为 0。

主程序

#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 - Preprocessing”部分下的项目构建设置中定义预处理器宏时,构建系统会按预期运行。-D它使用参数定义全局宏,clang使其可用于项目使用的任何文件。

但是,当我从终端使用 clang 时,源代码可以正确编译clang main.c

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

标签: ioscxcodemacosxcodebuild

解决方案


使用 Xcode IDE 构建时会发出警告:

Ambiguous expansion of macro 'OBJC_OLD_DISPATCH_PROTOTYPES'

并且输出确实是 0 直接使用 Xcode,但 1 和clang main.c. 不同之处在于 Xcode 默认使用带有已启用模块的 clang:如果您在命令行中启用模块,则会在命令行上收到相同的警告:

clang -fmodules main.c  

解决方案

在 Xcode 中,选择目标,转到“构建设置”选项卡,然后在“Apple Clang - 语言 - 模块”部分中,将“启用模块(C 和 Objective-C)”条目切换为“否”:

构建设置

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

解释:

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

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

所以你对 OBJC_OLD_DISPATCH_PROTOTYPES 有一个矛盾的定义。预编译模块对 OBJC_OLD_DISPATCH_PROTOTYPES 使用 0,然后将其重新定义为 1。

顺便说一句:如果你使用

#define OBJC_OLD_DISPATCH_PROTOTYPES 0

那么您使用与预编译模块相同的定义,因此即使启用了模块,也不会发出关于宏的模棱两可扩展的警告。

如果没有启用的模块,预处理器会包含文本、编译结果并返回预期的结果,即在 objc.h 中使用所需的 typedef。


推荐阅读