最近因为种种的机缘巧合,在阅读ReactiveCocoa的源码,然后就被它里面对宏的神奇使用所折服了,我想从此将踏上不归之路。。

RAC中有一个RACmetamacros.h文件,这个文件里定义了大量的宏,里面对宏的使用到了出神入化的地步,可以在预处理时就得到可变参数个数,可以在书写RAC(obj,x)的时候,就自动提示出obj对象的属性x。今天我们就简单的学习一些关于宏的基础知识,然后举几个例子来具体的分析一下。
首先是关于宏里面各种符号意义的介绍,下面所列出来的是相对比较常见的。
1 、 \的作用:换行,因为#define只在当前行起作用,为了看的更简单、更直观,可以使用\换行显示。
2、 #的作用:#的功能是将其后面的宏参数字符串化,简单地说就是宏变量的其左右加上双引号。
3、 ##的作用:它表示将两个参数连接起来这种运算。注意函数宏必须是有意义的运算,因此你不能直接写AB来连接两个参数,而需要写成例子中的A##B。
4、 的作用:用来声明变量(注意在宏里面 1 也是一个变量,可以看做是变量x1。。)
5 、 __VA_ARGS:总体来说就是将左边宏中
... 的内容原样抄写在右边 VA_ARGS 所在的位置。
6、FILE :宏在预编译时会替换成当前的源文件名。
7、 LINE:宏在预编译时会替换成当前的行号。
8、 FUNCTION:宏在预编译时会替换成当前的函数名称。
有了以上的这些基础知识,我们先来看一个比较简单的例子。(例子来源于孙源大神的一片文章,可惜没有注释,可能大伙看了也不明白是怎么出来的。。)
[Macro]预处理时计算可变参数个数
#define COUNT_PARMS2(_a1, _a2, _a3, _a4, _a5, RESULT, ...) RESULT
#define COUNT_PARMS(...) COUNT_PARMS2(__VA_ARGS__, 5, 4, 3, 2, 1)
int count = COUNT_PARMS(1,2,3); // 预处理时count==3
下面就来一步步分析一下是怎么在预处理的时候通过宏得到count的个数的。
1、COUNT_PARMS(1,2,3)中的1,2,3是可变参数,将1,2,3当做一个整体参数传入到COUNT_PARMS2(VA_ARGS, 5, 4, 3, 2, 1)中的VA_ARGS中,对应上面所列出的第5点规则:【 VA_ARGS 总体来说就是将左边宏中 ... 的内容原样抄写在右边
VA_ARGS 所在的位置】
所以此时COUNT_PARMS(1,2,3)就等价于COUNT_PARMS2(1,2,3, 5, 4, 3, 2, 1)。
2、看一下COUNT_PARMS2的定义:COUNT_PARMS2(a1, _a2, _a3, _a4, _a5, RESULT, ...) RESULT,其中前面的_a1, _a2, _a3, _a4, _a5, 相当于是变量,对应上面的第4条规则:【 的作用:用来声明变量(注意在宏里面 _1 也是一个变量,可以看做是变量x1。。)】
因为宏定义前5个是变量,所以会将COUNT_PARMS2(1,2,3, 5, 4, 3, 2, 1)中的1,2,3,5,4参数依次赋值给_a1, _a2, _a3, _a4, _a5, 此时COUNT_PARMS2(_a1, _a2, _a3, _a4, _a5, RESULT, ...)中RESULT对应的就是第6个参数3,后面的那个...可变参数对应的就是最后的两个参数2和1了,我们看到宏定义最后将RESULT返回,而这个RESULT恰好就是我们想要的结果了,这里思考一下如果我们求COUNT_PARMS(1,2,3,4,5,6,7)的可变参数个数的话,RESULT会是多少呢
带着疑问,我们继续看个更复杂一些的----ReactiveCocoa中的metamacro_argcount宏,这也是一个用来在预编译的时候求可变参数个数的宏。
先看一下它的定义
#define metamacro_argcount(...) metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define metamacro_at(N, ...) metamacro_concat(metamacro_at, N)(__VA_ARGS__)
#define metamacro_concat(A, B) metamacro_concat_(A, B)
#define metamacro_concat_(A, B) A ## B
#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)
#define metamacro_head(...) metamacro_head_(__VA_ARGS__, 0)
#define metamacro_head_(FIRST, ...) FIRST
我们假设int a = metamacro_argcount(1,2, 3);//a = 3
看这个是怎么一步一步的计算出来的。步骤跟上面的那个简单例子基本一致。
注意我们为了书写方便,在此用X替代20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
一、 把1,2,3当参数传入宏里面
metamacro_argcount(1,2,3)就相当于metamacro_at(20,1,2,3,X)
二、 看下metamacro_at宏的定义metamacro_at(N, ...),所以第一步中的20相当于这里的N,而1,2,3,X则对应宏中的可变参数 ...
三、 继续往下走,看metamacro_concat_(A, B) A ## B这个宏,对应上述的规则3,此处会将传入的A和B做一个拼接。而从第二步对应过来的话,那么metamacro_at对应的是A,而20对应的是B,将它们做一个拼接得到metamacro_at20,所以此时第二步中metamacro_concat(metamacro_at, N)(VA_ARGS),就等价于metamacro_at20(1,2,3,X)因为要将VA_ARGS替换成前面的可变参数1,2,3,X。此时我们将X展开,就会得到metamacro_at20(1,2,3,20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
四、 查看metamacro_at20宏发现里面20个变量,将上一步中的前20个变量依次代入即可。此时metamacro_at20宏中的...可变参数就是剩下的3,2,1了,这时候我们就得到得到metamacro_head(3,2,1)
五、继续往下看查看metamacro_head_(VA_ARGS, 0)的实现,将3,2,1 代入后得到metamacro_head_(3,2,1, 0)。这个宏返回的是第一个元素,而这个3正好是可变参数的个数了。
需要注意的一点是,我们所输入的可变参数的个数是有最大值限制的,比如刚刚RAC中的最大可变参数数目是20,如果我们输入的超过了20的话,那么就会出现错误。
如果看到这里,你没有蒙圈的话,那么恭喜你已经初步进入“宏”的奇妙漂流中了,之后会加入对它的数学分析。。
要相信这只是一扇打开更广阔世界的门而已。。