一直以来用宏定义#define也就是定义一些简单的常量,至多也就是定义一个函数,很少关注宏定义的用法。直到看到这样的代码:
#define PLAYSOUNDEFFECT(...) \ [[GameManager sharedGameManager] playSoundEffect:@#__VA_ARGS__]
不得不说宏定义很强大!宏定义的使用使得程序的编写更加的简便!
作为iOS开发者,有必要深入研究一下宏定义的用法。
最官方的关于宏的使用说明网址是:http://gcc.gnu.org/onlinedocs/cpp/Macros.html#Macros
在Apple的官网上可以找到GNU C 4.2 Preprocessor User Guide,发现和GNU官网的说明一模一样。因为Xcode的编译器就是基于GNU C 4.2预处理器,因此在Objective-C的开发环境中使用宏和在C/C++中使用是一模一样的。
下面的文字是阅读官方使用说明后的总结及翻译。(代码直接从官方使用说明摘录)
1、Macros 宏
官方解释:
A macro is a fragment of code which has been given a name. Whenever the name is used, it is replaced by the contents of the macro. There are two kinds of macros. They differ mostly in what they look like when they are used. Object-like macros resemble data objects when used, function-like macros resemble function calls.
有两种宏的类型,一种是类对象的宏,封装使用的数据对象,另一种是类函数的宏,封装函数的调用。在ObjC里面,那就是可以封装Method的使用,如文章一开始的代码
1.1 类对象的宏
最基本的使用:
#define BUFFER_SIZE 1024 foo = (char *) malloc (BUFFER_SIZE); foo = (char *) malloc (1024);
通常宏的名称都是用大写字母。
------------------------------------------------------------------------------------------------
#define NUMBERS 1, \ 2, \ 3 int x[] = { NUMBERS }; ==> int x[] = { 1, 2, 3 };
------------------------------------------------------------------------------------------------
C预处理器是按顺序读取程序,因此宏定义生效在宏定义之后。
foo = X; #define X 4 bar = X; produces foo = X; bar = 4;
------------------------------------------------------------------------------------------------
宏调用时,预处理器在替换宏的内容时,会继续检测内容本身是否也是宏定义,如果是,会继续替换内容。
#define TABLESIZE BUFSIZE #define BUFSIZE 1024 TABLESIZE ==> BUFSIZE ==> 1024------------------------------------------------------------------------------------------------
宏定义以最后生效的定义为准,因此下面的代码TABLESIZE对应37
#define BUFSIZE 1020 #define TABLESIZE BUFSIZE #undef BUFSIZE #define BUFSIZE 37
如果宏定义内容包含了名称,则预处理器会终止展开防止无限嵌套(infinite resursion)
1.2 类函数宏
#define lang_init() c_init() lang_init() ==> c_init()类函数宏的名称后面加了"()"。
#define lang_init () c_init() lang_init() ==> () c_init()()
1.3 宏参数
在类函数宏里面可以添加参数使得更像真正的函数
#define min(X, Y) ((X) < (Y) ? (X) : (Y)) x = min(a, b); ==> x = ((a) < (b) ? (a) : (b)); y = min(1, 2); ==> y = ((1) < (2) ? (1) : (2)); z = min(a + 28, *p); ==> z = ((a + 28) < (*p) ? (a + 28) : (*p));
------------------------------------------------------------------------------------------------
如何宏里面有字符串的内容,即使与参数名相同,也不会被替换。如下:
#define foo(x) x, "x" foo(bar) ==> bar, "x"
使用”#“预处理操作符来实现将宏中的参数转化为字符串。例子如下:
#define WARN_IF(EXP) \ do { if (EXP) \ fprintf (stderr, "Warning: " #EXP "\n"); } \ while (0) WARN_IF (x == 0); ==> do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);
然后没有什么方法可以直接将参数转化成单一的字符char
------------------------------------------------------------------------------------------------
#define xstr(s) str(s) #define str(s) #s #define foo 4 str (foo) ==> "foo" xstr (foo) ==> xstr (4) ==> str (4) ==> "4"
1.5 连接
使用"##"操作符可以实现宏中token的连接。
struct command { char *name; void (*function) (void); }; struct command commands[] = { { "quit", quit_command }, { "help", help_command }, ... }; #define COMMAND(NAME) { #NAME, NAME ## _command } struct command commands[] = { COMMAND (quit), COMMAND (help), ... };
#define eprintf(...) fprintf (stderr, __VA_ARGS__) eprintf ("%s:%d: ", input_file, lineno) ==> fprintf (stderr, "%s:%d: ", input_file, lineno)
#define eprintf(args...) fprintf (stderr, args)
#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__) eprintf ("success!\n") ==> fprintf(stderr, "success!\n");
#define FOO 4 x = FOO; ==> x = 4; #undef FOO x = FOO; ==> x = FOO; These definitions are effectively the same: #define FOUR (2 + 2) #define FOUR (2 + 2) #define FOUR (2 /* two */ + 2) but these are not: #define FOUR (2 + 2) #define FOUR ( 2+2 ) #define FOUR (2 * 2) #define FOUR(score,and,seven,years,ago) (2 + 2)