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

前言

相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏。但是很多人只知道它的强大威力,却没有意识到在特定环境下的危险性。

本文将通过代码测试的方式告诉读者,如何正确地使用这两个的宏。

@weakify 和 @strongify

本文意在说明其危险性,所以不会全面的讲解这两个宏。
如果您对其该兴趣,请参考其它作者的文章或者自行查看源码。

这两个宏的定义如下:

EXTScope.h#L45-L47

#define weakify(...)     rac_keywordify     metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)

EXTScope.h#L83-L88

#define strongify(...)     rac_keywordify     _Pragma("clang diagnostic push")     _Pragma("clang diagnostic ignored \"-Wshadow\"")     metamacro_foreach(rac_strongify_,, __VA_ARGS__)     _Pragma("clang diagnostic pop")

其中 rac_keywordify 的定义如下:
EXTScope.h#L114-L118

#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif

测试

下面是官方提供了一个示例代码。
示例代码中定义了一个 block,该 block 用于判断入参 obj 是否和 foofar 其中的任何一个对象相等并返回 YES 或 NO 。

id foo =[ [obj alloc]init];

id far =[ [obj alloc]init];

@weakify(foo,bar);

BOOL (^matchFooOrBar)(id) = ^ BOOL  (od obj){

  //but now,upon entry,“foo” and "bar" will stay alive until the block has

  //finished excuting

  @strongify(foo,bar);

  return [foo isEqual:obj] || [bar isEqual:obj];

};

测试代码一

为了方便测试,这里重写了 rac_keywordify 的定义。

{

  #undef rac_keywordify

  #define rac_keywordify autoreleasepool { }

  

id foo = [[NSObject alloc] init];
        id bar = [[NSObject alloc] init];

        @weakify(foo, bar);

        BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){
            @strongify(foo, bar);
            NSLog(@"%@,%@", foo, bar);
        };
    }

}

相信眼尖的读者一眼就能看出与上面代码的不同。
block缺少返回值

下面是 Xcode 的截图。Xcode 产生一个 Control reaches end of non-void block 的??错误提示。

测试代码二

为了方便测试,这里重写了 rac_keywordify 的定义。

  {
        #undef rac_keywordify

        #define rac_keywordify try { } @catch(...) {}

        id foo = [[NSObject alloc] init];
        id bar = [[NSObject alloc] init];

        @weakify(foo, bar);

        BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){
            @strongify(foo, bar);
            NSLog(@"%@,%@", foo, bar);
        };
    }

由上图可知,Xcode 丢失了错误提示的能力。

问题分析

在 Release 模式下,rac_keywordify 被定义为 #define rac_keywordify try { } @catch(...) {},经预处理器处理后,会转换为下面的代码

  id foo = [[NSObject alloc] init];
        id bar = [[NSObject alloc] init];

        @try { } @catch(...) {} __attribute__((objc_ownership(weak))) __typeof__(foo) foo_weak_ = (foo); __attribute__((objc_ownership(weak))) __typeof__(bar) bar_weak_ = (bar);;

        BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){
            @try { } @catch(...) {}
# 99 "/Users/L/Documents/workspace/.../AppDelegate.m"
#pragma clang diagnostic push
# 99 "/Users/L/Documents/workspace/.../AppDelegate.m"
#pragma clang diagnostic ignored "-Wshadow"
# 99 "/Users/L/Documents/workspace/.../AppDelegate.m"
 __attribute__((objc_ownership(strong))) __typeof__(foo) foo = foo_weak_; __attribute__((objc_ownership(strong))) __typeof__(bar) bar = bar_weak_;
# 99 "/Users/L/Documents/workspace/.../AppDelegate.m"
#pragma clang diagnostic pop
# 99 "/Users/L/Documents/workspace/.../AppDelegate.m"
;
            NSLog(@"%@,%@", foo, bar);
        };

@try { } @catch(...) {}被添加到了等式的前面。在这种情况下,Xcode 本身的错误提示能力能被抑制了,就如同源码的注释中提到的那样。
// 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.
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif

很多人都研究过这部分代码,但是大部分的人都得出类似于这样的结论。

这段宏定义中的代码开头都少了个@,使得weakify、strongify前面必须加上@,当然也只有这作用。 然后这里为什么要判断DEBUG呢?我也不知道,我觉得这里没必要这样判断。

判断DEBUG的作用在于,正常的开发模式都是在DEBUG模式下面进行的。这样可以保留 Xcode 提示错误的能力。

结论

请读者回想一下,你是否可以快速的判断出自己是否在 DEBUG模式下开发?如果回答是NO,请谨慎使用 @weakify 和 @strongify

修改开发模式

点击项目名称,在弹出框中,点击 Edit Scheme...

Paste_Image.png

在模态视图中,点击 Build Configuration 单选框

Paste_Image.png

				
时间: 2024-10-17 14:34:49

[HMLY]13.请谨慎使用 @weakify 和 @strongify的相关文章

请谨慎使用 @weakify 和 @strongify

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

深入研究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

剖析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 = ^{      

剖析@weakify 和 @strongify

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

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广泛应用而进一步

【裸机装系统】获取硬盘信息失败,请谨慎操作!

公司新买了两台服务器,需要安装操作系统. 我在安装其中一台进PE时,提示“获取硬盘信息失败,请谨慎操作!” 研究了半天,网上说是因为磁盘设置了RAID,于是我把RAID删除.按CTRL+R键到配置RAID的地方,有如下提示,如图 找了半天将RAID删除 删除RAID后,没有了上面的提示,可是PE依然提示“获取硬盘信息失败,请谨慎操作” 同事说要不用光盘引导安装,于是我拿了张光盘刻录了一个镜像,也还是不行,报下面的错误: 既然光盘也不行,我把BIOS的参数也都看了几遍,实在是没辙了,于是找公司买服

深入研究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的由来 循环引用的问题相信大家都很理

请谨慎使用Java基本类型的对象类

能使用基本类型的情况请尽量使用基本类型,避免使用对应的对象类.如:一般首荐int.long.float.double,尽量少使用Integer.Double.Float.Double类型.因为在某些情况下,Java会自动的装箱及拆箱操作,从而造成程序性能问题. 在我本地电脑运行如下两个程序,出现了较大的性能差异: public class LongTest {     public static void main(String[] args) {         long startTime=

请谨慎使用你的root权限

自己来公司1年多了,管的系统早就烂熟于心.真是到了出了什么问题闭着眼睛都知道是错在哪.而且自己平时在服务器上操作小心谨慎.从来不会有什么问题.今天下午,想把服务器上的东西备份一下.然后从远程传了一些东西到主服务器上的根下.用完之后准备把它删了.不知道脑子里在想什么.我只是想删除一个目录而已.但是直接打出了rm -rf *的命令.回车的那一瞬间,提示 虽然删了我知道怎么恢复.但是当时身体立马发热,感觉头上冒汗.立马按住ctrl+c停止.停止以后也没有用,因为执行命令已经提示没有那个文件或目录了.没