Objective-C预处理器不是编译器的一部分,而是编译过程中的一个单独步骤。 简单来说,Objective-C预处理器只是一个文本替换工具,它指示编译器在实际编译之前进行必要的预处理。 我们将Objective-C预处理器称为OCPP。
所有预处理器命令都以井号(#
)开头。它必须是第一个字符(前面不能有空格),并且为了便于阅读,预处理器指令应该从第一列开始。 以下部分列出了所有重要的预处理程序指令 -
编号 | 指令 | 描述 |
---|---|---|
1 | #define |
替换预处理器宏 |
2 | #include |
从另一个文件插入特定标头 |
3 | #undef |
未定义的预处理器宏 |
4 | #ifdef |
如果定义了此宏,则返回true |
5 | #ifndef |
如果未定义此宏,则返回true |
6 | #if |
测试编译时条件是否为true |
7 | #else |
#if 的替代方案 |
8 | #elif |
在#else 和 #if 中的一个语句 |
9 | #endif |
结束预处理器条件 |
10 | #error |
在stderr 上打印错误消息 |
11 | #pragma |
使用标准化方法向编译器发出特殊命令 |
分析以下示例以了解各种宏的指令。
#define MAX_ARRAY_LENGTH 20
该指令告诉OCPP 用20
替换MAX_ARRAY_LENGTH
宏标识。使用#define
定义常量来提高可读性。
#import <Foundation/Foundation.h> #include "myheader.h"
这些指令告诉OCPP 从Foundation Framework获取foundation.h
,并将文本添加到当前源文件中。 下一行告诉OCPP 从本地目录获取myheader.h
并将内容添加到当前源文件。
#undef FILE_SIZE #define FILE_SIZE 42
它告诉OCPP 取消定义现有的FILE_SIZE
,并将FILE_SIZE
定义为42
。
#ifndef MESSAGE #define MESSAGE "You wish!" #endif
它告诉OCPP仅在尚未定义MESSAGE
宏时才定义MESSAGE
。
#ifdef DEBUG /* Your debugging statements here */ #endif
它告诉OCPP如果定义了DEBUG
,则执行包含语句的过程。 如果在编译时将- DDEBUG
标志传递给gcc
编译器,这将非常有用。 它将定义DEBUG
,因此可以在编译期间动态打开和关闭调试。
ANSI C定义了许多宏。虽然每个都可用于编程,但不应直接修改预定义的宏。
编号 | 宏 | 描述 |
---|---|---|
1 | __DATE__ |
当前日期为“MMM DD YYYY” 格式的字符文字 |
2 | __TIME__ |
当前时间作为“HH:MM:SS” 格式的字符文字 |
3 | __FILE__ |
它包含当前文件名作为字符串文字。 |
4 | __LINE__ |
它包含当前行号作为十进制常量。 |
5 | __STDC__ |
当编译器符合ANSI标准时,定义为1 。 |
试试下面的例子代码 -
#import <Foundation/Foundation.h> int main() { NSLog(@"File :%s\n", __FILE__ ); NSLog(@"Date :%s\n", __DATE__ ); NSLog(@"Time :%s\n", __TIME__ ); NSLog(@"Line :%d\n", __LINE__ ); NSLog(@"ANSI :%d\n", __STDC__ ); return 0; }
当编译并执行文件main.m
中的上述代码时,它会产生以下结果 -
2018-11-15 08:44:54.041 main[50640] File :main.m 2018-11-15 08:44:54.042 main[50640] Date :Nov 15 2018 2018-11-15 08:44:54.042 main[50640] Time :08:44:52 2018-11-15 08:44:54.042 main[50640] Line :7 2018-11-15 08:44:54.043 main[50640] ANSI :1
Objective-C预处理器提供以下运算符来创建宏 -
宏通常必须包含在一行中。宏延续运算符用于继续对于单行来说太长的宏。 例如 -
#define message_for(a, b) \ NSLog(@#a " and " #b ": We love you!\n")
字符串化或数字符号运算符(#
)在宏定义中使用时,将宏参数转换为字符串常量。 此运算符只能在具有指定参数或参数列表的宏中使用。 例如 -
#import <Foundation/Foundation.h> #define message_for(a, b) \ NSLog(@#a " and " #b ": We love you!\n") int main(void) { message_for(Carole, Debra); return 0; }
执行上面示例代码,得到以下结果:
2018-11-15 08:56:38.088 main[98681] Carole and Debra: We love you!
宏定义中的令牌粘贴运算符(##
)组合了两个参数。 它允许将宏定义中的两个单独的标记连接到一个标记中。 例如 -
#import <Foundation/Foundation.h> #define tokenpaster(n) NSLog (@"token" #n " = %d", token##n) int main(void) { int token34 = 40; tokenpaster(34); return 0; }
执行上面示例代码,得到以下结果:
2018-11-15 08:58:04.872 main[138839] token34 = 40
它是如何发生的,因为这个例子导致预处理器的以下实际输出 -
NSLog (@"token34 = %d", token34);
此示例显示令牌##n
到token34
的串联,这里使用了stringize
和token-pasting
。
预处理器定义的运算符用于常量表达式,以确定是否使用#define
定义标识符。如果定义了指定的标识符,则该值为true
(非零)。 如果未定义符号,则值为false
(零)。 定义的运算符指定如下 -
#import <Foundation/Foundation.h> #if !defined (MESSAGE) #define MESSAGE "You wish!" #endif int main(void) { NSLog(@"Here is the message: %s\n", MESSAGE); return 0; }
执行上面示例代码,得到以下结果:
2018-11-15 09:04:30.790 main[31654] Here is the message: You wish!
OCPP的一个强大功能是使用参数化宏模拟函数的能力。 例如,可能需要一些代码来对数字进行平方,如下所示 -
int square(int x) { return x * x; }
可以使用宏重写上面的代码,如下所示 -
#define square(x) ((x) * (x))
必须先使用#define
指令定义带参数的宏,然后才能使用它们。 参数列表括在括号中,并且必须紧跟宏名称。 宏名称和左括号之间不允许有空格。 例如 -
#import <Foundation/Foundation.h> #define MAX(x,y) ((x) > (y) ? (x) : (y)) int main(void) { NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20)); return 0; }
执行上面示例代码,得到以下结果:
2018-11-15 09:08:15.586 main[64146] Max between 20 and 10 is 20