前言

相信大部分见过 @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

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

  1. 请谨慎使用 @weakify 和 @strongify

    来源:酷酷的哀殿 链接:http://www.jianshu.com/p/d8035216b257 前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很 ...

  2. [HMLY]10.深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用

    前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理.然而实际使用Block过程中,还是会遇到一些问题,比如Retain Circle的问题. 目录 1.Retain ...

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

    深入研究Block捕获外部变量和__block实现原理 EOCNetworkFetcher.h typedef void (^EOCNetworkFetcherCompletionHandler)(N ...

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

    深入研究Block捕获外部变量和__block实现原理 前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理.然而实际使用Block过程中,还是会遇到一些问题,比如R ...

  5. 剖析@weakify 和 @strongify

    前言 使用RAC的时候我们常会看到这两个宏@weakify(self).@strongify(self),用来防止使用block时出现引用闭环. 今天看YYKit的时候,看到里面也写了类似的宏,还是来 ...

  6. 剖析RAC中的@weakify、@strongify

    0.很长的前言 1.问题 2.RAC是怎么解决的 2.weakify.strongify的定义 预备知识 一层层展开weakify 3.RAC装逼宏 metamacro_argcount 的定义 me ...

  7. @weakify 与 @strongify 实现原理

    为了解决 Block 造成的循环引用,iOS 开发过程中常常使用 @weakify 与 @strongify 来解决这个问题.下面就来看下 @weakify 与 @strongify 的实现原理. 准 ...

  8. linux fork进程请谨慎多个进程/线程共享一个 socket连接,会出现多个进程响应串联的情况。

    昨天组内同学在使用php父子进程模式的时候遇到了一个比较诡异的问题 简单说来就是:因为fork,父子进程共享了一个redis连接.然后父子进程在发送了各自的redis请求分别获取到了对方的响应体. 复 ...

  9. 送大家几个cnblogs号,供快捷评论,请谨慎使用

    欢迎评论& 3461896724@qq.com互动 可以在我的首页看更多 #1先送大家几个号:(密码都是 MLdlight2020)请区分大小写(可以直接复制) 写过一篇 免费验证码接收网站& ...

随机推荐

  1. Linux Shell脚本入门--awk命令详解

    简单使用: awk :对于文件中一行行的独处来执行操作 . awk -F :'{print $1,$4}'   :使用‘:’来分割这一行,把这一行的第一第四个域打印出来 . 详细介绍: AWK命令介绍 ...

  2. PHP中的表单提交和获取

    在php中表单提交的方式有两种: 1.post提交,这种安全性较高. 2.get提交,他提交的是一个url地址,因此在从地址上面就可以看到许多信息,因此不安全. 每个表单<form>后面都 ...

  3. Linq to sql与EF零碎知识点总结

    ------------------------------第一天(2013-3-25) 1.ado.net实体模型,(Ef) 2.创建上下文对象: 调用相应方法,最后调用.savechanges() ...

  4. [转]【Android】9-patch图片以及例子说明

    1.何为9-patch? NinePatch图片以*.9.png结尾,和普通图片(png图片)的区别是四周多了一个边框(如下图所示): 采用NinePatch图片做背景,可使背景随着内容的拉伸(缩小) ...

  5. 配置Sublime Text 2 的Python运行环境

    Sublime Text 2作为一款轻量级的编辑器,特点鲜明,方便使用,愈发受到普罗大众的喜爱,我个人最近也开始用了起来.同时,我近段时间还在学习Python的相关东西,所以开始用ST2来写Pytho ...

  6. 史上最全的Matlab资源电子书教程和视频下载合集【超级推荐】

    收藏吧,网上搜集的,费了老大劲了,推荐给有需要的人,^_^.   MATLAB课件2007北京交通大学.zip 4.87 MB   A Guide to MATLAB for Beginners an ...

  7. C语言指针操作

    欢迎访问我的新博客:http://www.milkcu.com/blog/ 原文地址:http://www.milkcu.com/blog/archives/pointer-manipulation. ...

  8. 自定义HttpFilter模块完善

    自定义HttpFilter模块完善   背景 在12月由于要针对项目做用户操作日志,但不想在每个方法里去增加代码,写入用户日志.因为这样具体的方法违背职责单一的原则,若后期日志内容格式发生变更,或其他 ...

  9. Quartz.net 定时计划使用

    新建解决方案和工程Quartz.net 使用Power Shell 命令 Install-Package Quartz 导入Quartz.net程序集 新建一个计划TestJob using Syst ...

  10. java web项目基础

      listener,filter,servlet的初始化顺序 web.xml中可以配置如下信息: context-param,listener,filter,servlet. 他们的加载顺序和在we ...