“#”的迷雾

转载 2013年12月11日 15:19:26

转自:http://blog.xcodev.com/blog/2013/12/16/mists-of-the-sharp/


在代码中使用Autolayout时,大家都会使用NSDictionaryOfVariableBindings这个宏,这个宏可以生成一个变量名到变量值映射的Dictionary。比如NSDictionaryOfVariableBindings(button1, button2)将会生成一个{ @"button1" = button1, @"button2 = button2 }的Dictionary。它是怎么做到的呢?我们来看看这个宏的定义:

#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)

这个宏定义中有3个参数,后两个参数不难理解,但第一个参数中间有个#符号,语法上看起来比较怪异,这个是什么呢?以前在做越狱的mobilesubstrate开发时,其中定义的一堆宏频繁使用了这个符号,下面就来揭开#这个符号在宏定义中的迷雾。

预编译的一些知识

我们的代码在build时并不是直接进行编译的,在编译之前还进行了预编译处理。预编译会把include或import的文件导入到文件中,同时会将代码中用到的宏进行替换。注意宏是直接在代码中替换成宏的定义的,如果有嵌套也会逐层替换。

“#”指示一些预编译命令

预编译命令一般都是以#开头的,比如#include#import#if等,在这里就不一一说明了,本文主要说明一下#在宏定义里面的一些作用。

宏参数字符串化

在一个参数前加一个#,预处理时将会变成这个参数名的字符串常量,即字符串化(stringify)。比如:

#define GET_NAME(X) #X
int a = 0;
NSLog(@"%s",GET_NAME(a));      //output: "a"
NSLog(@"%s",GET_NAME(a+3));    //output: "a+3"

将会得到以下输出:

a
a+3

可以看出#,将参数原样转换成字符串常量,如果参数是一个表达式,那么输出这个表达式的原样字符串常量。

回头再看看NSDictionaryOfVariableBindings的定义:

#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)

如果这样生成两个button的映射:

NSDictionaryOfVariableBindings(button1, button2);

那么预编译时就会转换成:

_NSDictionaryOfVariableBindings(@"""button1, button2", button1, button2, nil);

由于两个常量字符串放在一起就是字符串常量串联,将变成两个字符串常量组合在一起的字符串常量,也就是上面是一个空字符串"""button1, button2"串联,所以上面的代码等价于:

_NSDictionaryOfVariableBindings(@"button1, button2", button1, button2, nil);

那么_NSDictionaryOfVariableBindings函数就可以将它的第一个参数按逗号,分割开作为key,后面就是各个key对应的值了。因此这段代码就创建了一个内容为{ @"button1" = button1, @"button2 = button2 }的Dictionary。

命名的串联

#在宏定义中的另一个作用就是用于命名的串联,用##就可以串联它左右两边的命名,比如以下代码:

#define CONCAT(X, Y) X ## Y
NSString *helloworld = @"Hello, world!";
NSLog(@"%@",CONCAT(hello, world)); //output: "Hello, world"

CONCAT(hello, world)实际被转换成helloworld。注意一下,因为宏是预编译阶段进行展开的,就是说在编译之前,因此代码中的helloworld即使没有定义其实也是没问题的,预编译处理后,这两个命名是不存在的。

可选可变参数

##在宏定义中可以放在__VA_ARGS__之前表示可变参数可以为空,否则的话可变参数至少为一个了。

#define MYLOG(format, ...) NSLog(format, ##__VA_ARGS__)
MYLOG(@"Don't make an error!");

上面代码中MLOG中只有一个参数,如果不加##,则MLOG至少需要两个参数,在Xcode里将会出现编译错误。


如何使用# ## ... _ _VA_ARGS_ _

 1.#假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的...
  • hxxiaopei
  • hxxiaopei
  • 2006-10-29 11:32:00
  • 10462

C,C++宏中#、##和__VA_ARGS__的理解

文中__FILE__与示例1的可以参见《使用ANSI C and Microsoft C++中常用的预定义宏》 宏中的#的功能是将其后面的宏参数进行字符串化操作(Stringizing operat...
  • wangkai_123456
  • wangkai_123456
  • 2017-07-12 12:56:34
  • 682

宏的高级使用--##,__VA_ARGS__, __FILE__, __FUNCTION__等

先说一下本文中会提到的内容:##,__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__等 宏变量: 先举一个例子,会用到上面这些宏: #define my...
  • yiya1989
  • yiya1989
  • 2012-08-10 11:23:24
  • 34195

【Unity】FOV战争迷雾

战争迷雾作为一种常用的游戏效果,被广泛的运用在RTS游戏和最近火热的moba类游戏中,在游戏中需要不断的进行探索和侦察来打开更多的视野区域,或者用来显示友方的位置,或者是躲避敌方等。 常见的...
  • mobilebbki399
  • mobilebbki399
  • 2017-11-04 16:45:45
  • 1397

穿越计算机的迷雾总结

其实比较郁闷,这本书大一的时候就有看到评论比较好,但是忘了看。再后来,也就是现在,大二下初,在豆瓣上看到了,这次立马买了一本来看,由于实在好奇这本书的内容,也好奇计算机一开始是怎么造出来的(也对继电器...
  • heisetiantang
  • heisetiantang
  • 2016-03-12 22:10:39
  • 1651

可变参数宏 printf(__VA_ARGS__)

原文地址:printf(__VA_ARGS__)" style="text-decoration:none">可变参数宏 printf(__VA_ARGS__)作者:如此如此如此 用可变参数...
  • xunmeng2002
  • xunmeng2002
  • 2015-02-25 15:03:52
  • 696

__VA_ARGS__用法(转)

自定义调试信息的输出   调试信息的输出方法有很多种, 例如直接用printf, 或者出错时使用perror, fprintf等将信息直接打印到终端上, 在Qt上面一般使用qDebug,而守护进...
  • qq_31856835
  • qq_31856835
  • 2017-06-07 09:56:19
  • 139

cocos2dx战争迷雾实现(lua)

之前在网上一直没搜到用coco2dx(lua)实现战争迷雾的文章,就有了下文。 tilemap的美术资源(战争迷雾) 战争迷雾的原理在网上已经有相关资料,就不补充了。 --战争迷雾用 --t1中索...
  • aschen518
  • aschen518
  • 2015-10-27 12:48:48
  • 592

2D游戏平滑的迷雾战争效果

最近刚好有做2D游戏的点光源效果,然后就扩展一下,研究了一下战争迷雾的效果。主要是想实现类似魔兽争霸那种人物走动,然后黑色的战争迷雾随着人物的移动渐渐打开的效果。使用具有渐变透明图片作为光源来使得战争...
  • sujun10
  • sujun10
  • 2017-03-08 09:51:40
  • 3123

__VA_ARGS__用法

自定义调试信息的输出   调试信息的输出方法有很多种,  例如直接用printf,  或者出错时使用perror, fprintf等将信息直接打印到终端上, 在Qt上面一般使用qDebug,而守...
  • midion9
  • midion9
  • 2016-01-06 11:51:41
  • 266
收藏助手
不良信息举报
您举报文章:“#”的迷雾
举报原因:
原因补充:

(最多只允许输入30个字)