盒子
盒子
文章目录
  1. 0x00
  2. 0x01
  3. 0x02
  4. 0x03

黑魔法之宏系列 不定参数

0x00

说到宏,大家都很熟悉,我们平常在 Cocoa 编程中,经常会定义这么一个方法,用于获取颜色:

1
2
3
#define RGB(r, g, b) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:1]

UIColor *color = RGB(255, 255, 255);

在我们定义这个宏的时候会确定它的参数,分别是 rgb 这三个参数。但是这样做就有一个很明显的缺陷了,参数固定了。

有一天,我们遇到一个需求,我们获取颜色不仅仅是 rgb 这三个参数,还需要传入一个 alpha 的值,这时候我们就得重新定义一个宏

1
2
// 像这样
#define RGBA(r, g, b, a) [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:(a)]

那么这就能解决我们的问题了。

可是,这样真的完美解决了吗?

0x01

在学习 ReactiveCocoa 的时候,第一次看到里面的宏,仿佛进入了一个新的世界,原来宏还可以这么写!?!?

1
2
@keypath(self.view);
@keypath(self.view, hidden);

回想到我们定义的用于获取颜色的宏,因为多了个 alpha,就得出现两个宏,两种使用方法。

那为什么我们不学习一下 RAC 这里面的黑魔法,顺便升级一下自己定义的宏,把它变为这样呢?

1
2
RGBA(r, g, b)
RGBA(r, g, b, a)

0x02

我们先看 @keypath(...) 这个宏的定义:

1
2
3
4
5
6
7
8
9
// RAC 参考的宏
#define keypath(...) \
metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__))

#define keypath1(PATH) \
(((void)(NO && ((void)PATH, NO)), strchr(# PATH, '.') + 1))

#define keypath2(OBJ, PATH) \
(((void)(NO && ((void)OBJ.PATH, NO)), # PATH))

很简单,我们模仿一下

1
2
3
4
5
6
7
8
#define RGBA(...) \
metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(RGBA3(__VA_ARGS__))(RGBA4(__VA_ARGS__))

#define RGBA3(r, g, b) \
[UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:1]

#define RGBA4(r, g, b, a) \
[UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:(a)]

看看使用

1
2
UIColor *color1 = RGBA(255,255,255);
UIColor *color2 = RGBA(255,255,255, 0.5);

完美!

0x03

回过头,我们看看具体的原理吧,所谓知其然需知其所以然。

@keypath(...) 定义的代码中,我们发现其中有两个关键宏:metamacro_if_eq(A, B)metamacro_argcount(...)

  • metamacro_if_eq(A, B)

    看这么宏的名字,我们可以猜测它的作用应该是判断,带着这个猜着继续看源码吧:

    1
    2
    #define metamacro_if_eq(A, B) \
    metamacro_concat(metamacro_if_eq, A)(B)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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_head(...) \
metamacro_head_(__VA_ARGS__, 0)

#define metamacro_head_(FIRST, ...) FIRST

#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
....
#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__)
支持一下
可以请我喝瓶可乐吗?