Weakify和strongify探究

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
__weak __typeof__(self) weakSelf = self;

@weakify则省事多了,真是不得不感叹:不想偷懒的程序员不是好Geek啊!

1
@weakify(self)

简洁的背后是宏的功劳,得到预编译后的代码,在DEBUG模式下显示如下。

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

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

1
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中被引用,因其是弱引用的关系存在被释放的风险。

1
2
3
4
5
__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是不可能独立出现的,编译也不会通过。

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

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

1
2
3
4
5
6
7
8
@weakify(self)
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
    @strongify(self)
    if(self){
        //安全生产,放心使用
    }
} );

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

1
2
3
4
5
6
#define strongify(…)     ext_keywordify     _Pragma(“clang diagnostic push”)     _Pragma(“clang diagnostic ignored \”-Wshadow\””)     metamacro_foreach(ext_strongify_,, __VA_ARGS__)     _Pragma(“clang diagnostic pop”)

对@weakify和@strongify的探究是一个很“漫长”的过程,期间查阅了很多资料,阅读了宏相关的文章,也没想到简简单单的两个宏可以延伸出这么多内容。好高兴啊,又吃成长快乐了!

参考资料:

  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
时间: 2024-10-21 04:48:02

Weakify和strongify探究的相关文章

[HMLY]13.请谨慎使用 @weakify 和 @strongify

前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很多人只知道它的强大威力,却没有意识到在特定环境下的危险性. 本文将通过代码测试的方式告诉读者,如何正确地使用这两个的宏. @weakify 和 @strongify 本文意在说明其危险性,所以不会全面的讲解这两个宏.如果您对其该兴趣,请参考其它作者的文章或者自行查看源码. 这两个宏的定义如下: EXTScope.h#L45-L47 #define weakify(...) rac_keywordi

剖析RAC中的@weakify、@strongify

0.很长的前言 1.问题 2.RAC是怎么解决的 2.weakify.strongify的定义 预备知识 一层层展开weakify 3.RAC装逼宏 metamacro_argcount 的定义 metamacro_foreach_cxt 的定义 RAC的宏装逼过程总结 0.很长的前言 在block语句块中,如果需引用self,而self对象中又持有block对象,就会造成循环引用循环引用(retain cycle),导致内存泄露,比如以下代码     self.block = ^{      

深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用(下)

深入研究Block捕获外部变量和__block实现原理 EOCNetworkFetcher.h typedef void (^EOCNetworkFetcherCompletionHandler)(NSData *data); @interface EOCNetworkFetcher : NSObject @property (nonatomic, strong, readonly) NSURL *url; - (id)initWithURL:(NSURL *)url; - (void)star

请谨慎使用 @weakify 和 @strongify

前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很多人只知道它的强大威力,却没有意识到在特定环境下的危险性. 本文将通过代码测试的方式告诉读者,如何正确地使用这两个的宏. @weakify 和 @strongify 本文意在说明其危险性,所以不会全面的讲解这两个宏.如果您对其该兴趣,请参考其它作者的文章或者自行查看源码. 这两个宏的定义如下: EXTScope.h #define weakify(...) \ rac_keywordify \ m

剖析@weakify 和 @strongify

前言 使用RAC的时候我们常会看到这两个宏@weakify(self).@strongify(self),用来防止使用block时出现引用闭环. 今天看YYKit的时候,看到里面也写了类似的宏,还是来谈谈这两个宏是怎么实现的吧. 正文 ## 宏定义代码 由于YYKit中的weakify.strongify相对比较简单,所以只剖析RAC(2.5)中的weakify.strongify. #define weakify(...) rac_keywordify metamacro_foreach_cx

深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用(上)

深入研究Block捕获外部变量和__block实现原理 前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理.然而实际使用Block过程中,还是会遇到一些问题,比如Retain Circle的问题. 目录 1.Retain Circle的由来 2.weak.strong的实现原理 3.weakSelf.strongSelf的用途 [email protected].@strongify实现原理 一. Retain Circle的由来 循环引用的问题相信大家都很理

循环引用,看我就对了

循环引用,看我就对了 我是一头来自北方的羊,咩-咩-咩-!谈到循环引用,不知道你能想到什么?可能是delegate为啥非得用weak修饰,可能是block为啥总是需要特殊对待,你也可能仅仅想到了一个weakSelf,因为它能帮你解决99%的关于循环引用的事情.本文中,我将谈一谈我对循环引用的看法. 一.循环引用的产生 1.基本知识 首先,得说下内存中和变量有关的分区:堆.栈.静态区.其中,栈和静态区是操作系统自己管理的,对程序员来说相对透明,所以,一般我们只需要关注堆的内存分配,而循环引用的产生

ReactiveCocoa内存管理

1.我们创建的管道是如何被保存的么? ReactiveCocoa设计的目的之一是允许这样一种编程样式,即管道可以匿名创建.到目前为止,我们的管道都是这么处理的.为了支持这种模式,ReactiveCocoa维护了一个全局的信号集合.如果信号有一个或多个订阅者,它就是可用的.如果所有订阅者都被移除了,信号就被释放了. 2.如何取消对信号的订阅? 在一个completed事件或error事件后,一个订阅者会自动将自己移除.手动移除可能通过RACDisposable来完成.RACSignal的所有订阅方

走进ReactiveCocoa的世界

在学习ReactiveCocoa之前,先学习一下概念 ReactiveCocoa 是一套开源的基于Cocoa的FRP框架 .FRP的全称是Functional Reactive Programming,中文译作函数式响应式编程,是RP(Reactive Programm,响应式编程)的FP(Functional Programming,函数式编程)实现.说起来很拗口.太多的细节不多讨论,我们先关注下FRP的FP特征. 函数式编程 函数式编程,简单来说,就是多使用匿名函数,将逻辑处理过程,以一系列