libextobjc 拾遗之再谈 weakify

关于 weakify 的文章已经很多了,就不介绍具体用法了,直接来看实现原理:

#define weakify(...) \
    ext_keywordify \
    metamacro_foreach_cxt(ext_weakify_,, __weak, __VA_ARGS__)

之前已经介绍过 ext_keywordify 宏了,这里先不管他,直接来看下面的一行。为了说明问题,我举一个实际的例子来演示 weakify 是如何完成的。

注意 weakify 的定义中,参数是 ...,说明这里可以接受可变参数,也就是可以同时 weakify 多个变量。先考虑一个简单场景,假设我们调用 weakify(self),代码将变成:

metamacro_foreach_cxt(ext_weakify_,, __weak, self)

来看下 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_foreach_cxt 居然还递归了。我们先不着急,分析下 metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__)) 到底是个啥。

metamacro_concat 这个宏很简单,就是把两个变量拼起来了,而 __VA_ARGS__ 也不难,表示的是所有可变参数,所以它的结果是 metamacro_foreach_cxtmetamacro_argcount(self) 的结果拼接在以前。

我们先望文生义,假设 metamacro_argcount 就是用来计算参数个数的,后面再研究它的实现流程。于是上文的结果变成了 metamacro_foreach_cxt1

再把参数带入进去,weakify(self) 经过处理已经变成了 metamacro_foreach_cxt1(ext_weakify_,, __weak, self)

再来看下 metamacro_foreach_cxt1 的定义:

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

于是带入各个参数,再次化简以后得到 ext_weakify_(0, __weak, self)

再来看看 ext_weakify_ 的定义:

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

带入参数后得到:__weak __typeof__(self) self__weak_ = (self); 也就是标准的 weak 变量的写法了。

现在加大难度,假设写的是 weakify(self, a),那么经过处理就会变成 metamacro_foreach_cxt2(ext_weakify_,, __weak, self, a)

观察下 metamacro_foreach_cxt2 的定义:

#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
    SEP \
    MACRO(1, CONTEXT, _1)
top Created with Sketch.