RAC中@weakify、@strongify的原理以及如何避免block中的retain cycle

1、block中的循环引用

在iOS开发中,如果self持有block,在block中对self进行显式或隐式的引用,都会出现retain cycle而导致内存泄漏,比如:

1
2
3
4
5
6
self.someBlock = ^{
// 显式引用self
[self doSomething];
// 隐式引用self,_someProperty为self的某个属性
_someProperty = xxx;
}

以前,我们是这样解决的:

1
2
3
4
5
__weak typeof(self) weakSelf = self;
self.someBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[self doSomething];
}

在block外部使用weakSelf定义self的弱引用对象,然后在block内部对weakSelf进行一次强引用(防止执行到block块中代码的时候self被提前释放导致weakSelf为nil),由于strongSelf定义为局部变量,当其作用域结束后,strongSelf自动释放对self的强引用,从而避免了retain cycle。

2、优雅的解决办法

上述写法虽然足够严谨而且能有效地解决问题,但是每次都要写一长串的weakSelf和strongSelf定义着实麻烦。我们先看看RAC是如何优雅地解决的:

1
2
3
4
5
@weakify(self);
self.someBlock = @{
@strongify(self);
[self doSomething];
}

是不是很神奇,self还可以原封不动地使用,下面我们看看@weakify(self)和@strongify(self)这两个宏究竟有何神奇之处。

3、@weakify(self)和@strongify(self)的作用

首先看下@weakify(self)和@strongify(self)究竟做了哪些事情,打开Xcode看看进行宏替换后的代码:

preprocess代码对比

​ ①、self_weak_部分与我们通常的写法一致:外部定义self的弱引用weakSelf,然后在block内部强引用这个weakSelf防止其提前释放;

​ ②、@autoreleasepool{}: weakify连同它前面的@一起组合成了@autoreleasepool{},虽然这个autoreleasepool啥事都没做。。。(后面进行分析)

​ ③、 __attribute__((objc_ownership(weak))) 这部分代码就是__weak被编译器替换后的结果(weakify这个宏的替换结果中包含__weak

4、@weakify(self)和@strongify(self)的原理

首先,我们对@weakify(self)一层层展开:

1
2
3
#define weakify(...) \
rac_keywordify \
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
1
2
3
4
5
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif

在宏中,表示多个参数,__VA_ARGS__则对应相应的参数。用上述定义进行替换后,@weakify(self)等同于下面的代码:

1
2
@autoreleasepool{}
metamacro_foreach_cxt(rac_weakify_,, __weak, self)

继续:

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

这里包括了几个宏:

  • metamacro_concat:
1
2
3
4
#define metamacro_concat(A, B) \
metamacro_concat_(A, B)

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

​ 可以看到metamacro_concat的最终转换成宏连接符##

  • metamacro_argcount:
1
2
3
4
5
#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__)

这个宏的作用是根据metamacro_argcount的参数个数,将其替换成相应的数字,比如:

metamacro_argcount(self)会被替换成1, 下面一步步进行分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
首先RAC定义了如下的一些宏:
#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__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

1、metamacro_argcount(self)替换成:
metamacro_at(20, self, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
2、上述宏替换后:
metamacro_concat(metamacro_at, 20)(self, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
3、上述宏替换后:
metamacro_at20(self, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
4、上述宏替换后:
metamacro_head(1)
5、上述宏替换后:
metamacro_head_(1, 0)
6、最终替换成:1

将这两个宏完成替换后得到:

1
metamacro_foreach_cxt1(rac_weakify_,, __weak, self)

RAC中还定义了从metamacro_foreach_cxt0到metamacro_foreach_cxt20的一组宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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)

#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
SEP \
MACRO(3, CONTEXT, _3)
......
#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \
metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
SEP \
MACRO(19, CONTEXT, _19)

将metamacro_foreach_cxt1替换后得到:

1
2
3
4
5
rac_weakify(0,__weak,self)

rac_weakify的定义如下:
#define rac_weakify_(INDEX, CONTEXT, VAR) \
CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

继续根据rac_weakify的定义替换后得到:

1
2
3
4
5
__weak __typeof__(self) self_weak_ = (self);

@weakify(self)的完整形式:
@autoreleasepool{}
__weak __typeof__(self) self_weak_ = (self);

大功告成!

同理,@strongify(self)这个宏最终展开成以下形式:

1
__strong __typeof__(self) self = self_weak_;