剖析@weakify 和 @strongify
前言
使用RAC的时候我们常会看到这两个宏@weakify(self)、@strongify(self),用来防止使用block时出现引用闭环。 今天看YYKit的时候,看到里面也写了类似的宏,还是来谈谈这两个宏是怎么实现的吧。
正文
## 宏定义代码 由于YYKit中的weakify、strongify相对比较简单,所以只剖析RAC(2.5)中的weakify、strongify。
#define weakify(...)
rac_keywordify
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
#define strongify(...)
rac_keywordify
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored "-Wshadow"")
metamacro_foreach(rac_strongify_,, __VA_ARGS__)
_Pragma("clang diagnostic pop")
一点一点剥开。
weakify
#define weakify(...)
rac_keywordify
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
rac_keywordify
#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呢?我也不知道,我觉得这里没必要这样判断。
metamacro_foreach_cxt(rac_weakify_,, weak, __VA_ARGS)
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...)
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
先看看最外面的宏定义metamacro_concat:
#define metamacro_concat(A, B)
metamacro_concat_(A, B)
#define metamacro_concat_(A, B) A ## B
这里有点弄不懂再套一层宏定义的意图,##的作用是连接两个运算符。
1.然后这个宏的实现变成了这样
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...)
metamacro_foreach_cxt##metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
MACRO、SEP、CONTEXT这三个参数先不管 __VA_ARGS__是可变参数,获取在...中传入的N个参数。
2.metamacro_argcount(__VA_ARGS__)
#define metamacro_argcount(...)
metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
metamacro_argcount这个宏的作用是获得参数的个数。 为什么能获得参数的个数呢? 我们看看是怎么实现的
#define metamacro_at(N, ...)
metamacro_concat(metamacro_at, N)(__VA_ARGS__)
拼接metamacro_at##N(传入的第一个值,这里是20)(VA_ARGS) 也就是:
metamacro_at20(__VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
metamacro_at20的实现这样的:
#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)
截取前20个数,剩下的传入metamacro_head
#define metamacro_head(...)
metamacro_head_(__VA_ARGS__, 0)
#define metamacro_head_(FIRST, ...) FIRST
metamacro_head的作用返回第一个参数。 打个比方:@weakify(self),
metamacro_at20(self,20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
截取前20个参数,那么就应该是metamacro_head_(1),那么返回的就是1。 多个参数同理。
3.回到metamacro_foreach_cxt
metamacro_foreach_cxt##metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
metamacro_argcount既然返回的是参数个数那么就可以变成这样:
metamacro_foreach_cxt##N(MACRO, SEP, CONTEXT, __VA_ARGS__)
N是参数个数 还是以@weakify(self)打比方:
metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, __VA_ARGS__)
metamacro_foreach_cxt1的实现是这样的
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
4.到了这里我们就要回到之前传入的三个参数了
metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)
代入这些参数就应该是
metamacro_foreach_cxt1(rac_weakify_, , __weak, self) rac_weakify_(0,__weak,self)
先不管rac_weakify_如何实现,我们再来看看N=20的时候是怎么实现的。
#define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19)
metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18)
SEP
MACRO(19, CONTEXT, _19)
这里就有点类似递归了,先rac_weakify_(0,__weak,_19), 然后把前19个数传入metamacro_foreach_cxt19,metamacro_foreach_cxt19会rac_weakify_(0,__weak,_18),然后把前18个数传入metamacro_foreach_cxt18…直到metamacro_foreach_cxt1。 而且它是存在保护机制的,要是N=0
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
就不做任何操作。 然后我们再来看rac_weakify_实现了什么。
rac_weakify_的实现
#define rac_weakify_(INDEX, CONTEXT, VAR)
CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
INDEX只是第几个的标记,在这里没实际作用。 拿@weakify(self)打比方: rac_weakify_(0,__weak,self) 就会变成这样:
__weak __typeof__ (self) self_weak_ = self;
self_weak_是弱化后的self。 @weakify(self)就这么实现了。
再看看strongify
#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
和之前同理,只是为了前面必须加@。
_Pragma语句
_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored "-Wshadow"")
_Pragma("clang diagnostic pop")
可能有人会不太了解_Pragma是什么,其实大家都用过它的前身:
#pragma xxxx xxxx
换过来就是:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic pop
这里的clang语句的作用:忽略当一个局部变量或类型声明遮盖另一个变量的警告 clang的对照表可以看fuckingclangwarnings
http://fuckingclangwarnings.com/
metamacro_foreach
拿@strongify(self)打比方,metamacro_foreach(rac_strongify_,, self)
#define metamacro_foreach(MACRO, SEP, ...)
metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
即:
metamacro_foreach_cxt(metamacro_foreach_iter,,rac_strongify_,self)
metamacro_foreach_cxt
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...)
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
即:
metamacro_foreach_cxt##1(metamacro_foreach_iter,,rac_strongify_,self)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
即:
metamacro_foreach_iter(0, rac_strongify_, self)
metamacro_foreach_iter
#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG)
即:
rac_strongify_(0,self)
rac_strongify_
#define rac_strongify_(INDEX, VAR)
__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
即:
__strong __typeof__(self) self = self_weak_;
这个时候你在项目中敲入:
__weak __typeof__ (self) self_weak_ = self;
__strong __typeof__(self) self = self_weak_;
你会发现你有一个错误,Redefinition of 'self',重新定义了自己。 一定要注意__strong __typeof__(self) self = self_weak_只能在block里面用。
__weak __typeof__ (self) self_weak_ = (self);
RootViewController * rootVC = [[RootViewController alloc]init];
[rootVC setBlock:^{
__strong __typeof__(self) self = self_weak_;
}];
RAC写法:
@weakify(self)
RootViewController * rootVC = [[RootViewController alloc]init];
[rootVC setBlock:^{
@strongify(self)
}];
后记
对宏的运用来说,可以说,RAC的作者是我目前见过最强的。 本以为剖析起来应该花不了多少功夫,可是着实绞尽脑汁了。 我尽量捋清思路,但也就这样了..大家将就着看吧.. 要是有哪里不对或者不好,欢迎指出!
剖析@weakify 和 @strongify的更多相关文章
- 剖析RAC中的@weakify、@strongify
0.很长的前言 1.问题 2.RAC是怎么解决的 2.weakify.strongify的定义 预备知识 一层层展开weakify 3.RAC装逼宏 metamacro_argcount 的定义 me ...
- 请谨慎使用 @weakify 和 @strongify
来源:酷酷的哀殿 链接:http://www.jianshu.com/p/d8035216b257 前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很 ...
- 深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用(下)
深入研究Block捕获外部变量和__block实现原理 EOCNetworkFetcher.h typedef void (^EOCNetworkFetcherCompletionHandler)(N ...
- 深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用(上)
深入研究Block捕获外部变量和__block实现原理 前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理.然而实际使用Block过程中,还是会遇到一些问题,比如R ...
- [HMLY]13.请谨慎使用 @weakify 和 @strongify
前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很多人只知道它的强大威力,却没有意识到在特定环境下的危险性. 本文将通过代码测试的方式告诉读者,如何正 ...
- [HMLY]10.深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用
前言 在上篇中,仔细分析了一下Block的实现原理以及__block捕获外部变量的原理.然而实际使用Block过程中,还是会遇到一些问题,比如Retain Circle的问题. 目录 1.Retain ...
- @weakify 与 @strongify 实现原理
为了解决 Block 造成的循环引用,iOS 开发过程中常常使用 @weakify 与 @strongify 来解决这个问题.下面就来看下 @weakify 与 @strongify 的实现原理. 准 ...
- @weakify, @strongify ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify
首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...
- ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify
首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...
随机推荐
- [原博客] POJ 2425 A Chess Game
题目链接题意:给定一个有向无环图(DAG),上面放有一些旗子,旗子可以重合,两个人轮流操作,每次可以把一个旗子从一个位置移动到相邻的位置,无法移动时输,询问先手是否必胜. 这道题可以把每个旗子看作单独 ...
- VS2010皮肤控件介绍
在我们平时使用的各种工具中,如QQ,迅雷,以及各种空间等,都提供了一些换肤功能,可以让我们选择各种我们喜欢的界面.本文就对VS中常用的窗口程序做一个简单的换肤,利用一个dll文件来进行实现. 首先我们 ...
- discuz论坛很给力
11.10:老彭在搭建好论坛. 11.17: 主网站导航中加入“论坛” 11.20: 使用“T客在线”的版本将论坛全新改版. 新版论坛非常大气,让网站增色不少.
- [topcoder]BestRoads
http://community.topcoder.com/stat?c=problem_statement&pm=10172&rd=13515 http://community.to ...
- Android实战之你应该使用哪个网络库?
前言 目前基本上每个应用都会使用HTTP/HTTPS协议来作为主要的传输协议来传输数据.即使你没有直接使用HTTP协议,也会有成堆的SDK会包含这些协议,譬如分析.Crash反馈等等.当然,目前也有很 ...
- 【HDOJ】3436 Queue-jumpers
离散化+伸展树. /* 3436 */ #include <iostream> #include <string> #include <map> #include ...
- mongodb windows下服务安装与卸载
安装服务.bat 内容: d:\mongodb2.4.3\bin\mongod.exe --dbpath d:\data\db --config d:\mongodb2.4.3\mongod.cfg ...
- 【转】xcode APP 打包以及提交apple审核详细流程(新版本更新提交审核)
原文网址:http://blog.csdn.net/mad1989/article/details/8167529 最近项目到了最后的阶段,测试完一切ok后,准备打包以及提交,不料看到网上众教程,好多 ...
- WinDbg配置和使用基础
WinDbg配置和使用基础 WinDbg是微软发布的一款相当优秀的源码级(source-level)调试工具,可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件. 1. WinDbg介 ...
- SLua 中使用 Lua 5.3 的编译工程
2016-03-05 更新: 之前编译的库,在 Android 下 Lua_Number 和 Lua_Integer 被编译为了32位,导致从 C# 到 Lua 过程中有64位到32位整型转换会出现溢 ...