剖析@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的作者是我目前见过最强的。 本以为剖析起来应该花不了多少功夫,可是着实绞尽脑汁了。 我尽量捋清思路,但也就这样了..大家将就着看吧.. 要是有哪里不对或者不好,欢迎指出!

时间: 2024-08-06 12:36:49

剖析@weakify 和 @strongify的相关文章

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

[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

请谨慎使用 @weakify 和 @strongify

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

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

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

ReactiveCocoa内存管理

1.我们创建的管道是如何被保存的么? ReactiveCocoa设计的目的之一是允许这样一种编程样式,即管道可以匿名创建.到目前为止,我们的管道都是这么处理的.为了支持这种模式,ReactiveCocoa维护了一个全局的信号集合.如果信号有一个或多个订阅者,它就是可用的.如果所有订阅者都被移除了,信号就被释放了. 2.如何取消对信号的订阅? 在一个completed事件或error事件后,一个订阅者会自动将自己移除.手动移除可能通过RACDisposable来完成.RACSignal的所有订阅方

走进ReactiveCocoa的世界

在学习ReactiveCocoa之前,先学习一下概念 ReactiveCocoa 是一套开源的基于Cocoa的FRP框架 .FRP的全称是Functional Reactive Programming,中文译作函数式响应式编程,是RP(Reactive Programm,响应式编程)的FP(Functional Programming,函数式编程)实现.说起来很拗口.太多的细节不多讨论,我们先关注下FRP的FP特征. 函数式编程 函数式编程,简单来说,就是多使用匿名函数,将逻辑处理过程,以一系列

RAC基础笔记(2)

ReactiveCocoa 常见类 信号源相关 RACStream An abstract class representing any stream of values. RACStream 是一个抽象类,是以 Monad (函数式编程语言)的概念为依据进行设计的,它代表的就是一个 Monad .有了 Monad 作为基石后,许多基于流的操作就可以被建立起来了,比如map . filter . zip 等. RACSignal 可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value)