http://each.dog/blog/2015/05/02/weakify-and-strongify/
http://www.jianshu.com/p/3d6c4416db5e
Weakify和strongify探究
@weakify和@strongify是一组非常简洁搭配使用的宏,用来避免因循环引用而导致内存泄露。由[libextobjc]开源项目提供(https://github.com/jspahrsummers/libextobjc),被ReactiveCocoa广泛应用而进一步被熟知。
由于之前对宏不甚了解,看这两个宏的实现时非常头大。好在猫神的宏定义的黑魔法 - 宏菜鸟起飞手册给了非常大的帮助,是篇非常好的入门文章,这里就不再累述了。优雅的宏能够帮我们节省工作量,实乃居家旅行之必备神器。
@weakiy和@strongify本质上是对传统方法的简化和强化。
传统的写法,获取self类型,声明weakSelf的变量并在最前面设置weak属性。
1 |
|
@weakify则省事多了,真是不得不感叹:不想偷懒的程序员不是好Geek啊!
1 |
|
简洁的背后是宏的功劳,得到预编译后的代码,在DEBUG模式下显示如下。
1 |
|
在Release模式下。和传统方式基本如出一辙。
1 |
|
只是开头的字段实在莫名其妙,光看代码完全不能理解其用意。搜了很多地方,最后在libextobjc宏定义的文件注释中找到了说明(论一手资料的重要性)。其作用仅仅是不让编译器产生warnings。在DEBUG模式下用autorelease来保持编译器分析的完成性,而在release环境下用try/catch是为了避免产生过多的autorelase pool影响性能。实乃没有良策的折中(这些都是哪门子的奇淫技巧啊)。
Details about the choice of backing keyword:
The use of @try/@catch/@finally can cause the compiler to suppress return-type warnings. The use of @autoreleasepool {} is not optimized away by the compiler,resulting in superfluous creation of autorelease pools.
Since neither option is perfect, and with no other alternatives, the compromise is to use @autorelease in DEBUG builds to maintain compiler analysis, and to use @try/@catch otherwise to avoid insertion of unnecessary autorelease pools.
weakSelf在Block中被引用,因其是弱引用的关系存在被释放的风险。
1 2 3 4 5 |
|
所以引入了@strongify,release环境下预编译后的代码如下。self_weak_赋值给新声明的局部变量self,此次赋值覆盖了全局的self,使self成为强引用局部变量,在函数执行完后才会释放,保证了运行中的安全。
注意self_weak是由@weakify生成的变量,@strongify中引用了该变量,在宏的实现中。self_weak隐藏在宏的实现中,非常隐晦。所以@strongify是不可能独立出现的,编译也不会通过。
1 2 |
|
但是在调用@strongify之前,self_weak_就有可能已经失效,所以进行一层判断还是挺有必要的。
1 2 3 4 5 6 7 8 |
|
最后@strongify还有一点值得注意的地方,在声明局部self变量时会出现编译器-Wshadow
的警告,此处进行了一层处理,选择无视该警告。
1 2 3 4 5 6 |
|
对@weakify和@strongify的探究是一个很“漫长”的过程,期间查阅了很多资料,阅读了宏相关的文章,也没想到简简单单的两个宏可以延伸出这么多内容。好高兴啊,又吃成长快乐了!
参考资料:
- suggest use strongify
- Asynchronous Design Patterns with Blocks, GCD, and XPC
- libextobjc
- how strongly works
- reactiveCoca weakify and strongify
- Making all self references in blocks weak by default