前言

使用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的更多相关文章

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

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

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

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

  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. [HMLY]13.请谨慎使用 @weakify 和 @strongify

    前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很多人只知道它的强大威力,却没有意识到在特定环境下的危险性. 本文将通过代码测试的方式告诉读者,如何正 ...

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

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

  7. @weakify 与 @strongify 实现原理

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

  8. @weakify, @strongify ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify

    首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...

  9. ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify

    首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...

随机推荐

  1. DZ的伪静态神马的终于OK了

    十分感谢开发“凤凰图集”的onexin团队!帮忙搞定了伪静态!这玩意儿折腾了俺好多功夫,人家瞬间就搞定.

  2. 使用 .NET 平台,如何玩转 Universal Windows 应用?

    2015年7月30日 本文作者是 Managed Languages 团队项目经理 Lucian Wischik. 不久前,Visual Studio 2015上新增 Windows 10 应用的开发 ...

  3. 基于Delphi的Socket I/O模型全接触 good

    老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系.他们的信会被邮递员投递到他们的信箱里. 这和Socket模型非常类似.下面我就以老陈接收信件为例讲解Socket I/O模型. 一:se ...

  4. jquery 提示插件 cluetip

    jquery的 插件cluetip, 地址下载是:plugins.learningjquery.com/cluetip/demo/ 下面简单讲解下用法: 1 首先当然要放JQUERY的基本JS,和这个 ...

  5. android 获取 imei号码 及相关信息

    android 获取 imei号码 参考:http://www.cnblogs.com/luxiaofeng54/archive/2011/03/01/1968063.html 核心代码: Imei ...

  6. android View 关于transient

    今天来研究一下 ListView 的删除动画 由于 ListView 卷动时会把画面上的 item 重用以显示不同数据 这样会导致我们可能会删除到非正确的 item 或是出现显示上的问题(该 item ...

  7. 灰度图像--图像增强 直方图均衡化(Histogram equalization)

    灰度图像--图像增强 直方图均衡化(Histogram equalization) 转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些 ...

  8. WordPress Platinum SEO插件跨站脚本漏洞

    漏洞名称: WordPress Platinum SEO插件跨站脚本漏洞 CNNVD编号: CNNVD-201309-398 发布时间: 2013-09-24 更新时间: 2013-09-24 危害等 ...

  9. java线程(2)-线程间通信

    方法一 通过访问共享变量的方式(注:需要处理同步问题) 方法二 通过管道流 其中方法一有两种实现方法,即 方法一a)通过内部类实现线程的共享变量  public class Innersharethr ...

  10. The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

    The server is temporarily unable to service your request due to maintenance downtime or capacity pro ...