https://onevcat.com/2014/01/black-magic-in-macro/
1. #的作用
2. ##的作用
3. VA_ARGS的作用
4. RAC()
4.1 函数功能
RAC(),有两种调用方式,一个是两个参数,一个是三个参数的。函数的第一个参数代表着要绑定的对象,第二个参数要绑定的对象的属性,第三个参数代表当追踪的结果为nil时,应该赋予的值。
这里就有一个很有趣的问题,ReactiveCocoa是如何做到用一个函数宏,自动识别参数个数,并调用正确的函数的呢?
4.2 实现解析
我们通过一个具体的例子来解析RAC()的宏定义魔法。我们在程序中如此调用:
RAC(self.startButton, enabled) = RACObserve(self.viewModel, canStartTimer);
我们来看下,RAC中的宏定义魔法,干了些什么。RAC(TARGET,...)第一步被解析为:
RAC(TARGET,...) metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
(RAC_(TARGET, __VA_ARGS__, nil)) \
(RAC_(TARGET, __VA_ARGS__))
其中,TARGET为self.startButton,那么metamacro_argcount(__VA_ARGS__) 从函数的名字,我们知道其作用是获取参数的个数。我们来看下具体是怎样获得参数的个数的。
本例中为
metamacro_argcount(enabled)
定义为
#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)
本例中为
metamacro_at(20,enabled,20,19,18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
其中
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__)
其中
metamacro_concat(metamacro_at, N)(__VA_ARGS__)
其中
metamacro_concat
的定义为
#define metamacro_concat(A, B) \
metamacro_concat_(A, B)
-->
#define metamacro_concat_(A, B) A ## B
因此,逆推回去
metamacro_concat(metamacro_at, N)(__VA_ARGS__)
-->
metamacro_atN(__VA_ARGS)
-->
metamacro_at20(__VA_ARGS)
本例中为
metamacro_at20(enabled,20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
拼接处的函数metamacro_at20
,其定义为
#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__)
本例中为
metamacro_head(1)
其中
metamacro_head(__VA_ARGS__)
其定义为
metamacro_head_(__VA_ARGS__, 0)
封装了一层,真实为
#define metamacro_head_(FIRST, ...) FIRST
本例中为 1
也就是metamacro_argcount(enabled) 的结果为 1。
回到RAC的第一层定义,现在的解析式为
metamacro_if_eq(1, 1) \
(RAC_(TARGET, __VA_ARGS__, nil)) \
(RAC_(TARGET, __VA_ARGS__))
下面分析metamacro_if_eq,其定义为
#define metamacro_if_eq(A, B) \
metamacro_concat(metamacro_if_eq, A)(B)
其中A写死为1,B本例中计算结果为1,本例中变为
metamacro_if_eq(1,1) (RAC_(TARGET, __VA_ARGS__, nil)) (RAC_(TARGET, __VA_ARGS__))
拼接结果为:
metamacro_if_eq1(B)
本例中为:
metamacro_if_eq1(1)
其定义为:
#define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE))
其中
#define metamacro_dec(VAL) \
metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
本例中为:
metamacro_at(1,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
拼接后为(metamacro_at的作用上面已经讲过了)
metamacro_at1(-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
其定义为
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
--> metamacro_head(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
--> metamacro_head_(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0)
--> 0
--> 合并
metamacro_if_eq0(0)
其中
#define metamacro_if_eq0(VALUE) \
metamacro_concat(metamacro_if_eq0_, VALUE)
本例中为metamacro_if_eq0_0。
回到原式,与后面的部分拼接结果为
#define RAC(TARGET, ...) \
metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
(RAC_(TARGET, __VA_ARGS__, nil)) \
(RAC_(TARGET, __VA_ARGS__))
--> metamacro_if_eq0_0 (RAC_(TARGET, __VA_ARGS__, nil)) (RAC_(TARGET, __VA_ARGS__))
metamacro_if_eq0_0定义为
#define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_
在本例中化为
(RAC_(TARGET, __VA_ARGS__, nil)) metamacro_consume_ (RAC_(TARGET, __VA_ARGS__))
--> (RAC_(TARGET, __VA_ARGS__, nil))
--> (RAC_(self.startButton, enabled, nil))
ps:
我们再看下,如果参数不为1的情况(这里的1的计数排除了第一项,在本例中排除的是self.startButton)。
如果是不止两个参数的情况那么,设参数个数为x,x<>1,则原式化为
metamacro_if_eq(1,x) (RAC_(TARGET, __VA_ARGS__, nil)) (RAC_(TARGET, __VA_ARGS__))
--> metamacro_if_eq1(x)
--> metamacro_at(x,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
这里假设x为2(注意这里x只要不是取1,宏定义展开都是一样的,而在RAC()这个宏,x只可能取1或2),则
--> metamacro_at2(-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
--> metamacro_head(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
--> 3
--> metamacro_if_eq0_3(RAC_(TARGET, __VA_ARGS__, nil)) (RAC_(self.startButton, enabled, nil))
由 metamacro_if_eq0_3()的定义,前面一项被吃掉了,所以原式只剩了后一项
--> (RAC_(self.startButton, enabled, nil))
至此,由RAC(...)至RAC_(...)的解析完毕!这个宏的作用就是根据参数的个数选择调用函数式子的不同,如果只有1个参数的,就调用(RAC_(self.startButton, enabled, nil)),如果有多个参数的就调用(RAC_(TARGET, __VA_ARGS__))。
5. RACObserve()
5.1. 函数的功能
让我们再看下4.2中提到的例子
RAC(self.startButton, enabled) = RACObserve(self.viewModel, canStartTimer);
等式的左边的意思是我关注的是self.startButton的enabled属性,我需要将其与self.viewModel的canStartTimer属性绑定起来。那么右边实际上是个KVO。一旦self.viewModel的canStartTimer属性发生变化,self.startButton的enabled相应的发生变化。
5.2 函数解析
让我们看下ReactiveCocoa是怎么做到的。
我们的例子是RACObserve(self.viewModel, canStartTimer);
RACObserve()的定义是
#define RACObserve(TARGET, KEYPATH) \
[(id)(TARGET) rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]
因此,在本例中解析为
[(id)(self.viewModel) rac_valuesForKeyPath:@keypath(self.viewModel, canStartTimer) observer:self]
OK,原来是self.viewModel发送rac_valuesForKeyPath: observer:消息。等等,@keypath是什么鬼?别急,@keypath定义如下:
#define keypath(...) \
metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__))
哈哈,原来是我们之前解析过的宏,那么很明显了,如果keypath中的参数个数等于1,那么调用keypath1,否则调用keypath2。在本例中,参数是2。因此应该调用keypath2。
#define keypath2(OBJ, PATH) \
(((void)(NO && ((void)OBJ.PATH, NO)), # PATH))
带入例子,化为
(((void)(NO && ((void)self.viewModel.canStartTimer, NO)), # canStartTimer))
处理一下#操作符,化为
(((void)(NO && ((void)self.viewModel.canStartTimer, NO)), "canStartTimer"))
TODO: @keyPath宏未能解析其生效的原理。根据其注解,这个宏的作用是验证参数的合法性,最后返回的结果是一个NSString,即keypath
因此,RACObserve()的作用就是发送rac_valuesForKeyPath: observer:消息。其中根据传入的keypath参数个数,做了一些处理。
接下来,我们分析一下rac_valuesForKeyPath: observer:的作用
其函数声明为:
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(NSObject *)observer;
定义为
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(NSObject *)observer {
return [[[self rac_valuesAndChangesForKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:observer] reduceEach:^(id value, NSDictionary *change) {
return value;
}] setNameWithFormat:@"RACObserve(%@, %@)", self.rac_description, keyPath];
}
6.@strongify, @ weakify & @unsafeify
@strongify的使用,必须在@weakify使用之后,是成对的操作
6.1 @weakify
weakify 定义如下
#define weakify(...) \
autoreleasepool {} \
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
我们的例子是
@weakify(self)
--> autoreleasepool {} \
metamacro_foreach_cxt(rac_weakify_,, __weak, self)
注意这里有两个非法的地方,
- rac_weakify_没有参数,这本来是非法的,但是此处编译器还不会检查,因此其真正的展开在之后,我们先列出其定义。
- metamacro_foreach_cxt(rac_weakify_,, __weak, self)中有两个连续逗号,第二个参数失踪了。这是放空的意思。注意,放空是可以的,但是要注意用法,否则编译器会报错。
#define rac_weakify_(INDEX, CONTEXT, VAR) \
CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
回到metamacro_foreach_cxt的定义为
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
本例中为
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
--> metamacro_argcount(__VA_ARGS__)的结果为1.
--> metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, __VA_ARGS__)
--> metamacro_foreach_cxt1(rac_weakify_, SEP, __weak, self)
metamacro_foreach_cxt1的定义为
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
则
metamacro_foreach_cxt1(rac_weakify_, SEP, __weak, self)
--> rac_weakify_(0,__weak,self)
rac_weakify_定义为
#define rac_weakify_(INDEX, CONTEXT, VAR) \
CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
--> __weak __typeof__(self) self_weak_ = (self);
至此,我们看到了@weakify的作用,其将传入的参数,自动创建一个__weak的对象。
TODO:
- 多参数传入的解析过程
- @autorelasepool并没有纳入任何东西。
- 莫名其妙的连续两个逗号。
我们继续来看一下,当weakify传入多个参数,如两个参数的时候,RAC是怎么处理的。
当weakify传入两个参数的时候,宏定义展开,某个阶段为metamacro_foreach_cxt2(rac_weakify_, SEP, __weak, __VA_ARGS__)其中__VA_ARGS__是两个参数,假设为self,和self.buttonmetamacro_foreach_cxt2的定义为
#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
SEP \
MACRO(1, CONTEXT, _1)
--> metamacro_foreach_cxt2(rac_weakify_, SEP, __weak, self, self.button)
--> 展开得 metamacro_foreach_cxt1(rac_weakify_, SEP, __weak, self) \
SEP \
rac_weakify_(1, __weak, self.button)
--> __weak __typeof__(self) self_weak_ = (self); __weak __typeof__(self.button) self.button_weak_ = (self.button);
6.2 @strongify