请谨慎使用 @weakify 和 @strongify

前言

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

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

@weakify 和 @strongify

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

这两个宏的定义如下:

EXTScope.h

#define weakify(...) \

rac_keywordify \

metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)

EXTScope.h

#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

#if DEBUG

#define rac_keywordify autoreleasepool {}

#else

#define rac_keywordify try {} <a href=‘http://www.jobbole.com/members/wx895846013‘>@catch</a> (...) {}

#endif

测试

下面是官方提供了一个示例代码。

示例代码中定义了一个 block,该 block 用于判断入参 obj 是否和 foo、far 其中的任何一个对象相等并返回 YES 或 NO 。

id foo = [[NSObject alloc] init];

id bar = [[NSObject alloc] init];

@weakify(foo, bar);

// this block will not keep ‘foo‘ or ‘bar‘ alive

BOOL (^matchesFooOrBar)(id) = ^ BOOL (id obj){

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

// finished executing

@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 的??错误提示。

错误提示.png

测试代码二

为了方便测试,这里重写了 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);

};

}

这份代码除了将 #define rac_keywordify autoreleasepool { } 修改为 #define rac_keywordify try { } @catch(...) {}以外,与上面的代码并没有不同。

理想的情况当然时,Xcode 依然有??错误提示。但是,现实往往是残酷的,Xcode 只提供了一个未使用变量的??。

无错误提示.png

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

问题分析

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

id foo = [[NSObject alloc] init];

id bar = [[NSObject alloc] init];

<a href=‘http://www.jobbole.com/members/xyz937134366‘>@try</a> { } @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){

<a href=‘http://www.jobbole.com/members/xyz937134366‘>@try</a> { } @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 {} <a href=‘http://www.jobbole.com/members/wx895846013‘>@catch</a> (...) {}

#endif

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

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

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

结论

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

修改开发模式

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

Paste_Image.png

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

Paste_Image.png

时间: 2024-10-27 04:24:32

请谨慎使用 @weakify 和 @strongify的相关文章

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

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

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

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

剖析@weakify 和 @strongify

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

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

公司新买了两台服务器,需要安装操作系统. 我在安装其中一台进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停止.停止以后也没有用,因为执行命令已经提示没有那个文件或目录了.没