1、block中的循环引用
在iOS开发中,如果self持有block,在block中对self进行显式或隐式的引用,都会出现retain cycle而导致内存泄漏,比如:
1 | self.someBlock = ^{ |
以前,我们是这样解决的:
1 | __weak typeof(self) weakSelf = self; |
在block外部使用weakSelf定义self的弱引用对象,然后在block内部对weakSelf进行一次强引用(防止执行到block块中代码的时候self被提前释放导致weakSelf为nil),由于strongSelf定义为局部变量,当其作用域结束后,strongSelf自动释放对self的强引用,从而避免了retain cycle。
2、优雅的解决办法
上述写法虽然足够严谨而且能有效地解决问题,但是每次都要写一长串的weakSelf和strongSelf定义着实麻烦。我们先看看RAC是如何优雅地解决的:
1 | @weakify(self); |
是不是很神奇,self还可以原封不动地使用,下面我们看看@weakify(self)和@strongify(self)这两个宏究竟有何神奇之处。
3、@weakify(self)和@strongify(self)的作用
首先看下@weakify(self)和@strongify(self)究竟做了哪些事情,打开Xcode看看进行宏替换后的代码:
①、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 |
|
1 |
在宏中,…
表示多个参数,__VA_ARGS__
则对应相应的参数。用上述定义进行替换后,@weakify(self)等同于下面的代码:
1 | @autoreleasepool{} |
继续:
1 |
|
这里包括了几个宏:
- metamacro_concat:
1 |
|
可以看到metamacro_concat的最终转换成宏连接符##
- metamacro_argcount:
1 |
|
这个宏的作用是根据metamacro_argcount的参数个数,将其替换成相应的数字,比如:
metamacro_argcount(self)会被替换成1, 下面一步步进行分析:
1 | 首先RAC定义了如下的一些宏: |
将这两个宏完成替换后得到:
1 | metamacro_foreach_cxt1(rac_weakify_,, __weak, self) |
RAC中还定义了从metamacro_foreach_cxt0到metamacro_foreach_cxt20的一组宏:
1 |
|
将metamacro_foreach_cxt1替换后得到:
1 | rac_weakify(0,__weak,self) |
继续根据rac_weakify的定义替换后得到:
1 | __weak __typeof__(self) self_weak_ = (self); |
大功告成!
同理,@strongify(self)这个宏最终展开成以下形式:
1 | __strong __typeof__(self) self = self_weak_; |