RAC中的宏定义魔法

2019/01/21 00:13 上午 posted in  iOS

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)

注意这里有两个非法的地方,

  1. rac_weakify_没有参数,这本来是非法的,但是此处编译器还不会检查,因此其真正的展开在之后,我们先列出其定义。
  2. 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:

  1. 多参数传入的解析过程
  2. @autorelasepool并没有纳入任何东西。
  3. 莫名其妙的连续两个逗号。
    我们继续来看一下,当weakify传入多个参数,如两个参数的时候,RAC是怎么处理的。
    当weakify传入两个参数的时候,宏定义展开,某个阶段为 metamacro_foreach_cxt2(rac_weakify_, SEP, __weak, __VA_ARGS__) 其中__VA_ARGS__是两个参数,假设为self,和self.button metamacro_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