@weakify和@strongify是一组非常简洁搭配使用的宏,用来避免因循环引用而导致内存泄露。由开源项目libextobjc提供,被ReactiveCocoa广泛应用而进一步被熟知。

由于之前对宏不甚了解,看这两个宏的实现时非常头大。好在猫神的宏定义的黑魔法 - 宏菜鸟起飞手册给了非常大的帮助,是篇非常好的入门文章,这里就不再累述了。优雅的宏能够帮我们节省工作量,实乃居家旅行之必备神器。

@weakiy和@strongify本质上是对传统方法的简化和强化

传统的写法,获取self类型,声明weakSelf的变量并在最前面设置weak属性。

__weak __typeof__(self) weakSelf = self;

@weakify则省事多了,真是不得不感叹:不想偷懒的程序员不是好Geek啊! 简洁的背后是宏的功劳,得到预编译后的代码,在DEBUG模式下显示如下。

@autoreleasepool {} __attribute__((objc_ownership(weak))) __typeof__(self) self_weak_ = (self);

在Release模式下。和传统方式基本如出一辙。

try {} @catch (…) {} __attribute__((objc_ownership(weak))) __typeof__(self) self_weak_ = (self);

只是开头的字段实在莫名其妙,光看代码完全不能理解其用意。搜了很多地方,最后在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中被引用,因其是弱引用的关系存在被释放的风险。

__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
[weakSelf doSomething];//原有对象被释放,weakSelf有变成nil的风险 (待考证释放和nil是什么样的过程)
} );

所以引入了@strongify,release环境下预编译后的代码如下。self_weak_赋值给新声明的局部变量self,此次赋值覆盖了全局的self,使self成为强引用局部变量,在函数执行完后才会释放,保证了运行中的安全。

注意self_weak_是由@weakify生成的变量,@strongify中引用了该变量,在宏的实现中。self_weak_隐藏在宏的实现中,非常隐晦。所以@strongify是不可能独立出现的,编译也不会通过。

@try {} @finally {}
 __attribute__((objc_ownership(strong))) __typeof__(self) self = self_weak_; 

但是在调用@strongify之前,self_weak_就有可能已经失效,所以进行一层判断还是挺有必要的。

@weakify(self)
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
    @strongify(self)
    if(self){
        //安全生产,放心使用
    }
} );

最后@strongify还有一点值得注意的地方,在声明局部self变量时会出现编译器-Wshadow的警告,此处进行了一层处理,选择无视该警告。

#define strongify(…) \
    ext_keywordify \
    _Pragma(“clang diagnostic push”) \
    _Pragma(“clang diagnostic ignored \”-Wshadow\””) \
    metamacro_foreach(ext_strongify_,, __VA_ARGS__) \
    _Pragma(“clang diagnostic pop”)

参考资料:

  1. suggest use strongify
  2. Asynchronous Design Patterns with Blocks, GCD, and XPC
  3. libextobjc
  4. how strongly works
  5. reactiveCoca weakify and strongify
  6. Making all self references in blocks weak by default