RAC中宏的解析

2017-12-29  本文已影响110人  lattr

一入宏门深似海
从此迷路出不来

按照惯例先上代码

    @weakify(self)
    [[self.btn rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
        @strongify(self)
        self.label.text = @"hello";
    }];

这是RAC中最常用的宏的使用。大家都知道需要成对使用,可传多个参数,为什么呢?点开源宏(不是袁弘)进行分析。

#define weakify(...) \
    rac_keywordify \
    metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)

其中...代表可变参数,即可传入多个值,对应于上面的__VA_ARGS__。比如@weakify(self, _a, _b)编译时__VA_ARGS__就会替换为self, _a, _b。现在分成三个部分来解析上面的宏。

1.rac_keywordify

#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif

这里比较简单,如果是DEBUG模式,就开启自动释放池,如果是release模式,就使用@try/@catch。作者在注释中说明了,这两种方式都是完美的,并且没有其他方式,之所以在DEBUG模式下使用自动释放池,是因为@try/@catch会影响XCode的警告功能,导致返回值的检查出问题。比如下面的例子,在release模式下,没有返回值,也不会出现编译错误。

@weakify(self);
self.block = ^BOOL {
  @strongify(self);
  NSLog(@"123");
};

所以,按@weakify(self, _a, _b)举例子,编译后的结果为@autoreleasepool {} metamacro_foreach_cxt(rac_weakify_,, __weak, self, _a, _b)

2.metamacro_foreach_cxt

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

#define metamacro_concat(A, B) \
        metamacro_concat_(A, B)

#define metamacro_concat_(A, B) A ## B

#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_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

metamacro_foreach_cxt展开之后又出现两个宏metamacro_concatmetamacro_argcount。现在分别分析。

#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)

#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
    SEP \
    MACRO(1, CONTEXT, _1)

#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
    metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    SEP \
    MACRO(2, CONTEXT, _2)

由于SEP这个参数传的是空,所以该参数可忽略不计。根据上面的宏定义
->metamacro_for_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2)
->metamacro_for_cxt2(MACRO, SEP, CONTEXT, _0, _1) MACRO(2, CONTEXT, _2)
->metamacro_for_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(1, CONTEXT, _1) MACRO(2, CONTEXT, _2)
->MACRO(0, CONTEXT, _0) MACRO(1, CONTEXT) MACRO(2, CONTEXT)
->rac_weakify_(0, __weak, self) rac_weakify_(1, __weak, _a) rac_weakify_(2, __weak, _b)

3.rac_weakify_

#define rac_weakify_(INDEX, CONTEXT, VAR) \
    CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

所以继续转化
->CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR)
->__weak __typeof__(self) metamacro_concat(self, _weak_) = (self)
->__weak __typeof__(self) self_weak_ = (self)
所以得到最终结果:
->rac_weakify_(0, __weak, self) rac_weakify_(1, __weak, _a) rac_weakify_(2, __weak, _b)
->__weak __typeof__(self) self_weak_ = (self) __weak __typeof__(_a) _a_weak_ = (_a) __weak __typeof__(_b) _b_weak_ = (_b)

使用XCode验证结果如下:

       @autoreleasepool {} __attribute__((objc_ownership(weak))) __typeof__(self) self_weak_ = (self); __attribute__((objc_ownership(weak))) __typeof__(_a) _a_weak_ = (_a); __attribute__((objc_ownership(weak))) __typeof__(_b) _b_weak_ = (_b);
    [[self.btn rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
        @autoreleasepool {}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
 __attribute__((objc_ownership(strong))) __typeof__(self) self = self_weak_; __attribute__((objc_ownership(strong))) __typeof__(_a) _a = _a_weak_; __attribute__((objc_ownership(strong))) __typeof__(_b) _b = _b_weak_;
#pragma clang diagnostic pop
 self.label.text = @"hello";
        _a = @"a";
        _b = @"b";
    }];

__weak经过LLVM会自动转化为__attribute__((objc_ownership(weak)))

上一篇下一篇

猜你喜欢

热点阅读