一入宏門深似海
從此迷路出不來(lái)
按照慣例先上代碼
@weakify(self)
[[self.btn rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self)
self.label.text = @"hello";
}];
這是RAC中最常用的宏的使用几于。大家都知道需要成對(duì)使用话侧,可傳多個(gè)參數(shù)佛析,為什么呢益老?點(diǎn)開源宏(不是袁弘)進(jìn)行分析。
#define weakify(...) \
rac_keywordify \
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
其中...
代表可變參數(shù)寸莫,即可傳入多個(gè)值捺萌,對(duì)應(yīng)于上面的__VA_ARGS__
。比如@weakify(self, _a, _b)
編譯時(shí)__VA_ARGS__
就會(huì)替換為self, _a, _b
”炀ィ現(xiàn)在分成三個(gè)部分來(lái)解析上面的宏桃纯。
1.rac_keywordify
。
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif
這里比較簡(jiǎn)單披坏,如果是DEBUG
模式态坦,就開啟自動(dòng)釋放池,如果是release
模式刮萌,就使用@try/@catch
驮配。作者在注釋中說(shuō)明了,這兩種方式都是完美的着茸,并且沒有其他方式壮锻,之所以在DEBUG
模式下使用自動(dòng)釋放池,是因?yàn)?code>@try/@catch會(huì)影響XCode的警告功能涮阔,導(dǎo)致返回值的檢查出問題猜绣。比如下面的例子,在release
模式下敬特,沒有返回值掰邢,也不會(huì)出現(xiàn)編譯錯(cuò)誤。
@weakify(self);
self.block = ^BOOL {
@strongify(self);
NSLog(@"123");
};
所以伟阔,按@weakify(self, _a, _b)
舉例子辣之,編譯后的結(jié)果為@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
展開之后又出現(xiàn)兩個(gè)宏metamacro_concat
和metamacro_argcount
』彻溃現(xiàn)在分別分析。
-
metamacro_concat
A ## B的意思是連接A和B合搅。比如metamacro_concat(n, 1)
編譯后為n1多搀。 -
metamacro_argcount
metamacro_argcount
的作用是返回傳入的可變參數(shù)的個(gè)數(shù)。
展開為:
->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, ...
一一對(duì)應(yīng)康铭,而_0, _1, _2, _3, _4, _5, _6, ···, _17, _18, _19
為20個(gè)固定參數(shù),所以20, self, _a, _b, 20, 19, 18, 17, ···, 6, 5, 4
這20個(gè)參數(shù)被固定參數(shù)所取代赌髓,所以只剩下3从藤,2催跪,1
對(duì)應(yīng)于...
。
->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)
返回的結(jié)果為1
叠荠。
metamacro_foreach_cxt(rac_weakify_,, __weak, self, _a, _b)
就可簡(jiǎn)化為
->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
這個(gè)參數(shù)傳的是空,所以該參數(shù)可忽略不計(jì)扫责。根據(jù)上面的宏定義
->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);
所以繼續(xù)轉(zhuǎn)化
->CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR)
->__weak __typeof__(self) metamacro_concat(self, _weak_) = (self)
->__weak __typeof__(self) self_weak_ = (self)
所以得到最終結(jié)果:
->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驗(yàn)證結(jié)果如下:
@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
經(jīng)過(guò)LLVM會(huì)自動(dòng)轉(zhuǎn)化為__attribute__((objc_ownership(weak)))
。