美文网首页
RAC中宏的解析

RAC中宏的解析

作者: lattr | 来源:发表于2017-12-29 23:15 被阅读110次

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

    按照惯例先上代码

        @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。现在分别分析。

    • metamacro_concat
      A ## B的意思是连接A和B。比如metamacro_concat(n, 1)编译后为n1。
    • metamacro_argcount
      metamacro_argcount的作用是返回传入的可变参数的个数。
      展开为:
      ->metamacro_at(20, self, _a, _b, 20, 19, 18, 17, ···, 6, 5, 4, 3, 2, 1)
      由于20, self, _a, _b, 20, 19, 18, 17, ···, 6, 5, 4, 3, 2, 1_0, _1, _2, _3, _4, _5, _6, ···, _17, _18, _19, ...一一对应,而_0, _1, _2, _3, _4, _5, _6, ···, _17, _18, _19为20个固定参数,所以20, self, _a, _b, 20, 19, 18, 17, ···, 6, 5, 4这20个参数被固定参数所取代,所以只剩下3,2,1对应于...
      ->metamacro_concat(metamacro_at, 20)(self, _a, _b, 20, 19, 18, 17, ···, 6, 5, 4, 3, 2, 1)
      ->metamacro_at20(self, _a, _b, 20, 19, 18, 17,···, 6, 5, 4, ...) metamacro_head(3, 2, 1)
      ->metamacro_at20(self, _a, _b, 20, 19, 18, 17,···, 6, 5, 4, ...) metamacro_head(3, 2, 1)
      ->metamacro_head_(3, 2, 1, 0)
      ->3
      所以如果按@weakify(self)为例,metamacro_argcount(self)返回的结果为1
      metamacro_foreach_cxt(rac_weakify_,, __weak, self, _a, _b)就可简化为
      ->metamacro_concat(metamacro_foreach_cxt, 3)(MACRO, SEP, CONTEXT, __VA_ARGS__)
      ->metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, __VA_ARGS__)
      ->metamacro_foreach_cxt3(rac_weakify_,, __weak, self, _a, _b)
    #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)))

    相关文章

      网友评论

          本文标题:RAC中宏的解析

          本文链接:https://www.haomeiwen.com/subject/sddvgxtx.html