XCode Build System:是否弄混了预处理程序定义和包含的头文件?

问题描述

一个问题。

我在XCode Build System方面遇到了一些麻烦,特别是在预处理器定义方面。

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

但这就是开始变得怪异的地方了!

根本无法识别宏,并且好像没有#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-预处理”部分下定义预处理器宏时,构建系统将按预期方式运行。它使用-D的{​​{1}}参数定义全局宏,从而使该宏可用于项目使用的任何文件

但是,当我在使用clang的终端上使用clang时,源代码可以正确编译。

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

解决方法

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

Ambiguous expansion of macro 'OBJC_OLD_DISPATCH_PROTOTYPES'

,直接使用Xcode的输出确实为0,而使用clang main.c的输出为1。区别在于Xcode默认情况下对启用了模块的模块使用clang:如果在此处启用模块,则会在命令行上收到相同的警告:

clang -fmodules main.c  

解决方案

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

build settings

然后,无论在命令行上使用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。