一入宏门深似海
从此迷路出不来
按照惯例先上代码
@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_concat
和metamacro_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)))
。