深入 Block
Block 前言
Block
是OC
中对C
语言的扩展功能,是一种带有自动变量的匿名函数,Block
在OC
中的实现,点击前往源码页面
/* Revised new layout. */
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
从Block_layout
很容易看到isa
,可知OC
处理block
是按照对象来处理的。isa
常见的就是_NSConcreteStackBlock
,_NSConcreteMallocBlock
,_NSConcreteGlobalBlock
下面来研究一下Block
的捕获外部变量的特性以及__block
的实现原理
Block 怎么获取外部变量值
说到变量,一共有以下几种:
- 自动变量
- 函数参数(block捕获外部变量 要除去函数参数一项)
- 静态变量
- 静态全局变量
- 全局变量
测试代码:
static int global_static_int = 1;
int global_int = 1;
int main(int argc, const char * argv[]) {
@autoreleasepool {
static int static_int = 1;
int var_int = 1;
void (^block)(void) = ^{
static_int ++;
//var_int ++; // 报错:不可赋值(缺少__block类型说明符)
global_int ++;
global_static_int ++;
NSLog(@"in block global_static_int = %d\n, global_int = %d \n,static_int = %d \n,var_int = %d",global_static_int,global_int,static_int,var_int);
};
static_int ++;
var_int ++;
global_int ++;
global_static_int ++;
NSLog(@"out block global_static_int = %d\n, global_int = %d \n,static_int = %d \n,var_int = %d",global_static_int,global_int,static_int,var_int);
NSLog(@"**************** 分割线 ****************");
block();
}
return 0;
}
运行结果
out block
global_static_int = 2
, global_int = 2
,static_int = 2
,var_int = 2
**************** 分割线 ****************
in block global_static_int = 3
, global_int = 3
,static_int = 3
,var_int = 1
由此得出以下结论:
- 为什么在Block里面不加__block不允许改变变量
- 为什么
var_int
变量的值没有增加,其他几个会增加
为了研究实现原理,我们通过以下clang命令,将OC转化为C/C++语言来研究Block各个特性的实现方式
clang -rewrite-objc main.m
转换后的源码如下(clang转换后.cpp 文件后,代码有10w多行,取自身需要的源码:):
static int global_static_int = 1;
int global_int = 1;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *static_int;
int var_int;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_int, int _var_int, int flags=0) : static_int(_static_int), var_int(_var_int) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *static_int = __cself->static_int; // bound by copy
int var_int = __cself->var_int; // bound by copy
(*static_int) ++;
global_int ++;
global_static_int ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_0,global_static_int,global_int,(*static_int),var_int);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
static int static_int = 1;
int var_int = 1;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_int, var_int));
static_int ++;
var_int ++;
global_int ++;
global_static_int ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_1,global_static_int,global_int,static_int,var_int);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_2);
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
首先全局变量global_int
静态全局变量global_static_int
的值增加,以及它们被Block
捕获进去,这一点很好理解,因为是全局的,作用域很广,所以Block
捕获了它们进去之后,在Block
里面进行++操作,Block
结束之后,它们的值依旧可以得以保存下来
在__main_block_impl_0
中,可以看到静态变量static_int
和自动变量var_int
,被Block
从外面捕获进来,成为__main_block_impl_0
这个结构体的成员变量,构造函数如下
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_int, int _var_int, int flags=0) : static_int(_static_int), var_int(_var_int) // 自动变量和静态变量被捕获称为成员变量追加到构造函数后
main
里面的block
中的__main_block_impl_0
初始化如下:
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_k, val));
impl.isa = &_NSConcreteStackBlock;
impl.Flags = 0;
impl.FuncPtr = __main_block_impl_0;
Desc = &__main_block_desc_0_DATA;
* _static_int = 1;
var_int = 1;
到此,__main_block_impl_0
结构体就是这样把自动变量捕获进来的。也就是说,在执行Block
语法的时候,Block
语法表达式所使用的自动变量的值是被保存进了Block
的结构体实例中,也就是Block
自身中,注:block
使用不到的值,并不会去捕获
我们再来看__main_block_func_0
这个函数:
__main_block_func_0(struct __main_block_impl_0 *__cself) {
int *static_int = __cself->static_int; // bound by copy
int var_int = __cself->var_int; // bound by copy
(*static_int) ++;
global_int ++;
global_static_int ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_e46fc9_mi_0,global_static_int,global_int,(*static_int),var_int);
}
bound by copy
,自动变量val_int
虽然被捕获进来了,但是是用 __cself->val
来访问的。Block
仅仅捕获了val
的值,并没有捕获val_int
的内存地址。所以在__main_block_func_0
这个函数中即使我们重写这个自动变量val_int
的值,依旧没法去改变Block
外面自动变量val_int
的值,这就是之前在block
中使用var_int
会报错的原因。
自动变量是以值传递方式传递到
Block
的构造函数里面去的。Block
只捕获Block
中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block
内部不能改变自动变量的值。Block
捕获的外表变量可以改变值的是静态变量,静态全局变量,全局变量。上面例子也都证明过了。
回到上面的例子,4种变量里面只有静态变量,静态全局变量,全局变量这3种是可以在Block
里面被改变值的。仔细观看源码,我们能看出这3个变量可以改变值的原因。
- 静态全局变量,全局变量由于作用域的原因,于是可以直接在Block里面被改变。他们也都存储在全局区
- 静态变量传递给
Block
是内存地址值,所以能在Block
里面直接改变值。
在
Block
中改变变量值有2种方式,
一. 是传递内存地址指针到Block
中,
二. 是改变存储区方式(__block
)
Block的copy 与dispose
OC
中,一般Block
就分为以下3种,_NSConcreteStackBlock
,_NSConcreteMallocBlock
,_NSConcreteGlobalBlock
。哪3者的区别呢。
- 从捕获变量来说
- _NSConcreteStackBlock
只用到外部局部变量,成员属性变量,且没有强指针引用的都是Stack Block
,声明周期由系统控制,一旦返回就销毁 - _NSConcreteMallocBlock
有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制 - _NSConcreteGlobalBlock
没有用到外界变量或只用到全局变量、静态变量的block
为_NSConcreteGlobalBlock
,生命周期从创建到应用程序结束
- 从持有对象的角度上来看
- _NSConcreteStackBlock不持有对象
- _NSConcreteMallocBlock持有对象的
- _NSConcreteGlobalBlock 不持有对象
_NSConcreteStackBlock
所属的变量域一旦结束,那么该Block
就会被销毁。在ARC
环境下,编译器会自动的判断,把Block
自动的从栈copy
到堆。比如当Block
作为函数返回值的时候,肯定会copy
到堆上
以下四种方法系统都会默认调用Block
的copy
方法
- 手动调用
copy
-
Block
是函数的返回值` -
Block
被强引用,Block
被赋值给__strong
或者id
类型 - 调用系统API入参中含有
usingBlcok
的方法
copy
函数把Block
从栈上拷贝到堆上,dispose
函数是把堆上的函数在废弃的时候销毁掉
以下源码中2个常用的宏定义和4个常用的方法
#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#define Block_release(...) _Block_release((const void *)(__VA_ARGS__))
// Create a heap based copy of a Block or simply add a reference to an existing one.
// This must be paired with Block_release to recover memory, even when running
// under Objective-C Garbage Collection.
BLOCK_EXPORT void *_Block_copy(const void *aBlock)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
// Lose the reference, and if heap based and last reference, recover the memory
BLOCK_EXPORT void _Block_release(const void *aBlock)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
// Used by the compiler. Do not call this function yourself.
BLOCK_EXPORT void _Block_object_assign(void *, const void *, const int)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
// Used by the compiler. Do not call this function yourself.
BLOCK_EXPORT void _Block_object_dispose(const void *, const int)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
以下为Block_copy
的一个实现,实现了从_NSConcreteStackBlock
复制到_NSConcreteMallocBlock
的过程。对应有9个步骤
static void *_Block_copy_internal(const void *arg, const int flags) {
struct Block_layout *aBlock;
const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
// 1
if (!arg) return NULL;
// 2
aBlock = (struct Block_layout *)arg;
// 3
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
// 4
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
// 5
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return (void *)0;
// 6
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// 7
result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 1;
// 8
result->isa = _NSConcreteMallocBlock;
// 9
if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
return result;
}
以下为Block_release
的实现,实现了怎么释放一个Block。对应有6个步骤
void _Block_release(void *arg) {
// 1
struct Block_layout *aBlock = (struct Block_layout *)arg;
if (!aBlock) return;
// 2
int32_t newCount;
newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK;
// 3
if (newCount > 0) return;
// 4
if (aBlock->flags & BLOCK_NEEDS_FREE) {
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock);
_Block_deallocator(aBlock);
}
// 5
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
;
}
// 6
else {
printf("Block_release called upon a stack Block: %p, ignored\n", (void *)aBlock);
}
}
当使用,字符串的例子中来,转换源码之后,我们会发现多了一个copy
和dispose方法。
因为在C语言的结构体中,编译器没法很好的进行初始化和销毁操作。这样对内存管理来说是很不方便的。所以就在 __main_block_desc_0
结构体中间增加成员变量 void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*)
和void (*dispose)(struct __main_block_impl_0*)
,利用OC
的Runtime
进行内存管理。
相应的增加了2个方法。
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);}
这里的_Block_object_assign
和_Block_object_dispose
就对应着retain
和release
方法。
BLOCK_FIELD_IS_OBJECT
是Block
截获对象时候的特殊标示,如果是截获的__block
,那么是BLOCK_FIELD_IS_BYREF
Block中__block实现原理
- 普通非对象的变量
struct __Block_byref_var_int_0 {
void *__isa;
__Block_byref_var_int_0 *__forwarding;
int __flags;
int __size;
int var_int;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_var_int_0 *var_int; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_int_0 *_var_int, int flags=0) : var_int(_var_int->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_var_int_0 *var_int = __cself->var_int; // bound by ref
(var_int->__forwarding->var_int) ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_97bc98_mi_0, (var_int->__forwarding->var_int));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->var_int, (void*)src->var_int, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->var_int, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_var_int_0 var_int = {(void*)0,(__Block_byref_var_int_0 *)&var_int, 0, sizeof(__Block_byref_var_int_0), 1};
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_var_int_0 *)&var_int, 570425344));
(var_int.__forwarding->var_int) ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_97bc98_mi_1, (var_int.__forwarding->var_int));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_97bc98_mi_2);
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
从源码我们能发现,带有__block
的变量也被转化成了一个结构体__Block_byref_var_int_0
,这个结构体有5个成员变量。第一个是isa
指针,第二个是指向自身类型的__forwarding
指针,第三个是一个标记flag
,第四个是它的大小,第五个是变量值,名字和变量名同名
__attribute__((__blocks__(byref))) __Block_byref_var_int_0 var_int = {(void*)0,(__Block_byref_var_int_0 *)&var_int, 0, sizeof(__Block_byref_var_int_0), 1};
源码中是这样初始化的。__forwarding
指针初始化传递的是自己的地址。然而这里__forwarding
指针真的永远指向自己么?
我们把Block
拷贝到了堆上,这个时候打印出来的2个i变量的地址就不同了
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int var_int = 1;
NSLog(@"%p",&var_int);
void (^block)(void) = [^{
var_int ++;
NSLog(@"%p",&var_int);
} copy];
NSLog(@"**************** 分割线 ****************");
block();
}
return 0;
}
地址不同就可以很明显的说明__forwarding
指针并没有指向之前的自己了。那__forwarding
指针现在指向到哪里了呢?__block
现在应该也在堆上。
前面,堆上的Block
会持有对象。我们把Block
通过copy
到了堆上,堆上也会重新复制一份Block
,并且该Block
也会继续持有该__block
。当Block
释放的时候,__block
没有被任何对象引用,也会被释放销毁。
__forwarding
指针这里的作用就是针对堆的Block
,把原来__forwarding
指针指向自己,换成指向_NSConcreteMallocBlock
上复制之后的__block
自己。然后堆上的变量的__forwarding
再指向自己。这样不管__block
怎么复制到堆上,还是在栈上,都可以通过(i->__forwarding->i)
来访问到变量值。
ARC
环境下,一旦Block
赋值就会触发copy
,__block
就会copy
到堆上,Block
也是__NSMallocBlock
。ARC
环境下也是存在__NSStackBlock
的时候,这种情况下,__block
就在栈上。
MRC
环境下,只有copy
,__block
才会被复制到堆上,否则,__block
一直都在栈上,block
也只是__NSStackBlock
,这个时候__forwarding
指针就只指向自己了。
所以在__main_block_func_0
函数里面就是写的(i->__forwarding->i)
。
这里还有一个需要注意的地方。还是从例子说起:
- 对象的变量
// ARC
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block id block_obj = [[NSObject alloc]init];
id obj = [[NSObject alloc]init];
NSLog(@"block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
void (^myBlock)(void) = ^{
NSLog(@"***Block中****block_obj = [%@ , %p] , obj = [%@ , %p]",block_obj , &block_obj , obj , &obj);
};
myBlock();
}
return 0;
}
输出
2019-02-26 17:51:41.380964+0800 BLock[67095:13613488] block_obj = [<NSObject: 0x100625f30> , 0x7ffeefbff578] , obj = [<NSObject: 0x100625d40> , 0x7ffeefbff548]
2019-02-26 17:51:41.381288+0800 BLock[67095:13613488] ***Block中****block_obj = [<NSObject: 0x100625f30> , 0x100624638] , obj = [<NSObject: 0x100625d40> , 0x100624400]
同样来看源码:
struct __Block_byref_block_obj_0 {
void *__isa;
__Block_byref_block_obj_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
id block_obj;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
id obj;
__Block_byref_block_obj_0 *block_obj; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _obj, __Block_byref_block_obj_0 *_block_obj, int flags=0) : obj(_obj), block_obj(_block_obj->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_block_obj_0 *block_obj = __cself->block_obj; // bound by ref
id obj = __cself->obj; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_538cd5_mi_1,(block_obj->__forwarding->block_obj) , &(block_obj->__forwarding->block_obj) , obj , &obj);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->block_obj, (void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->block_obj, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_block_obj_0 block_obj = {(void*)0,(__Block_byref_block_obj_0 *)&block_obj, 33554432, sizeof(__Block_byref_block_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
id obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_lf_6jrk2sf11bd4pjvkrxfm_z4h0000gn_T_main_538cd5_mi_0,(block_obj.__forwarding->block_obj) , &(block_obj.__forwarding->block_obj) , obj , &obj);
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, (__Block_byref_block_obj_0 *)&block_obj, 570425344));
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
首先需要说明的一点是对象在OC中,默认声明自带__strong所有权修饰符的
根据打印出来的结果来看,ARC
环境下,Block
捕获外部对象变量,是都会copy
一份的,地址都不同。只不过带有__block
修饰符的变量会被捕获到Block
内部持有。
我们再来看看MRC环境下的情况,还是将上述代码的例子运行在MRC中。
输出:
2019-02-26 17:59:16.199394+0800 BLock[67192:13619032] block_obj = [<NSObject: 0x10306c950> , 0x7ffeefbff578] , obj = [<NSObject: 0x10306c760> , 0x7ffeefbff548]
2019-02-26 17:59:16.199719+0800 BLock[67192:13619032] ***Block中****block_obj = [<NSObject: 0x10306c950> , 0x7ffeefbff578] , obj = [<NSObject: 0x10306c760> , 0x7ffeefbff520]
这个时候block
在栈上,__NSStackBlock__
,可以打印出来retainCount
值都是1。当把这个block copy
一下,就变成__NSMallocBlock__
,对象的retainCount值就会变成2了。
在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象,所以才会产生循环引用的问题!
在ARC环境下,对于没有声明为__block的外部对象,也会被retain。
循环引用的由来
当A对象里面强引用了B对象,B对象又强引用了A对象,两者的retainCount
值一直都无法为0,内存始终无法释放,导致内存泄露,就是本应该释放的对象,在其生命周期结束之后依旧存在。三个对象之间也可能存在相互引用,也有一个对象自身的循环引用。当一个对象内部的一个obj
,强引用的自身,也会导致循环引用的问题出现。常见的就是block
里面引用的问题
_weak, _strong 实现原理
ARC
环境下,id
类型和对象类型和C
语言其他类型不同,类型前必须加上所有权的修饰符:
-
__strong
修饰符(默认) -
__weak
修饰符 -
__unsafe_unretained
修饰符 -
__autoreleasing
修饰符
__strong 的实现原理(详见ARC详细)
- 对象持有自己
id __strong strong_obj =[ [NSObject alloc] init];
clang 以下得到的结果
id __attribute__((objc_ownership(strong))) strong_obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
相应的会调用
id strong_obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(strong_obj,selector(init));
objc_release(strong_obj); // ARC 下会自动插入Release代码,在作用域结束的时候自动释放
- 对象不持有自己
生成对象的时候不用alloc/new/copy/mutableCopy
等方法
{
id __strong array_obj = [NSMutableArray array];
}
clang
一下
id __attribute__((objc_ownership(strong))) strong_obj = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));
相应的调用
id array_obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(array_obj);
objc_release(array_obj);
与之前对象会持有自己的情况不同,这里多了一个objc_retainAutoreleasedReturnValue
函数
在ARC详细文档中可知objc_retainAutoreleasedReturnValue
函数是LLVM编译器的一个优化objc_retainAutoreleasedReturnValue
,是用于自己持有(retain
)对象的函数,它持有的对象应为返回注册在autoreleasepool
中对象的方法或者是函数的返回值
还有俩个函数和
objc_retainAutoreleasedReturnValue
功能类似
1id objc_autoreleaseReturnValue(id value)
2id objc_retainAutoreleasedReturnValue(id value)
在ARC
中原本对象生成之后是要注册到autoreleasepool
中,但是调用了objc_autoreleasedReturnValue
之后,紧接着调用了objc_retainAutoreleasedReturnValue
,objc_autoreleasedReturnValue
函数会去检查该函数方法或者函数调用方的执行命令列表,如果里面有objc_retainAutoreleasedReturnValue()
方法,那么该对象就直接返回给方法或者函数的调用方。达到了即使对象不注册到autoreleasepool
中,也可以返回拿到相应的对象
__weak实现原理
{
id __weak weak_obj = strongObj; // 声明一个weak对象
}
clang 一下
id __attribute__((objc_ownership(none))) weak_obj = strongObj;
相应调用
id weak_obj;
objc_initWeak(& weak_obj,strongObj);
objc_destoryWeak(& weak_obj);
根据ARC
文档描述objc_initWeak
函数实现如下:
id objc_initWeak(id *object, id value) {
*object = nil;
return objc_storeWeak(object, value);
}
把传入的object
变成0或者nil,然后执行objc_storeWeak
函数
objc_storeWeak
函数实现为:
void objc_destroyWeak(id *object) {
objc_storeWeak(object, nil);
}
也是会去调用objc_storeWeak
函数。objc_initWeak
和objc_destroyWeak
函数都会去调用objc_storeWeak
函数,唯一不同的是调用的入参不同,一个是value
,一个是nil
。
objc_storeWeak
函数实现
objc_storeWeak
函数的用途就很明显了。由于weak
表也是用Hash table
实现的,所以objc_storeWeak
函数就把第一个入参的变量地址注册到weak
表中,然后根据第二个入参来决定是否移除。如果第二个参数为0
,那么就把__weak
变量从weak
表中删除记录,并从引用计数表中删除对应的键值记录。
所以如果__weak
引用的原对象如果被释放了,那么对应的__weak
对象就会被指为nil
。原来就是通过objc_storeWeak
函数这些函数来实现的
weakSelf、strongSelf的用途
先看一个循环引用的例子
.h文件
#import <Foundation/Foundation.h>
typedef void(^Run)();
@interface Person : NSObject
@property (copy , nonatomic) NSString *name;
@property (copy , nonatomic) Run run;
@end
.m文件
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc]init];
person.name = @"Mr.Peng";
person.run = ^{
NSLog(@"%@ start running", person.name);
};
}
到这里,大家应该看出来了,这里肯定出现了循环引用了。person
的run
的Block
里面强引用了student
自身。根据block
的分析,可知,_NSConcreteMallocBlock
捕获了外部的对象,会在内部持有它。retainCount
值会加一(可以通过Leak Checks
检查工具查看相互引用的对象)
而将run
修改为如下时
person.run = ^(NSString *name){
NSLog(@"%@ start running",name);
};
不会引起循环引用,因为block
不会捕获形参到block
内部进行持有
再看下面的例子:
@interface ViewController ()
@property (copy,nonatomic) NSString *name;
@property (strong, nonatomic) Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person * person = [[Person alloc]init];
self.name = @"halfrost";
self.person = person;
person.run = ^{
NSLog(@"%@ start running", self.name);
};
person.run();
}
ViewController
虽然强引用着person
,但是person
里面的blcok
强引用的是viewController
的name
属性,并没有形成环。如果把上述的self.name
改成self
,也依旧不会产生循环引用。因为他们都没有强引用这个block
有如下代码:
Person * person = [[Person alloc]init];
__block Person * bperson = person;
person.name = @"Mr.Peng";
person.run = ^{
NSLog(@"%@ start running", bperson.name);
bperson = nil;
};
这段代码会循环么,看起来不会,但实际上会,由于没有执行person
的block
,person
持有block
,block
持有__block
变量,__block
变量又持有student
对象,形成环,导致循环引用,想要解决循环引用,断掉其中的环就行了person.run()
执行block
即可
** 值得注意的是,在
ARC
下__block
会导致对象被retain
,有可能导致循环引用。而在MRC
下,则不会retain
这个对象,也不会导致循环引用
正式来看看weakSelf
,strongSelf
的用法
- weakSelf
#define WEAKSELF typeof(self) __weak weakSelf = self; 这是我们平时的写法。
Person * person = [[Person alloc]init];
__block Person * bperson = person;
person.name = @"Mr.Peng";
person.run = ^{
NSLog(@"%@ start running", bperson.name);
};
person.run()
这段代码因为block
还持有bperson
,会引起循环,改成weakSelf
模式:
Person * person = [[Person alloc]init];
__weak typeof(person) weakSelf = student;
person.name = @"Mr.Peng";
person.run = ^{
NSLog(@"%@ start running", weakSelf.name);
};
person.run()
解决循环应用的问题一定要分析清楚哪里出现了循环引用,只需要把其中一环加上
weakSelf
这类似的宏,就可以解决循环引用。如果分析不清楚,就只能无脑添加weakSelf
、strongSelf
,这样的做法不可取
- strongSelf
上面介绍完了weakSelf,既然weakSelf
能完美解决循环引用问题,那为何还需要strongSelf
呢?
Person * person = [[Person alloc]init];
__weak typeof(person) weakSelf = student;
person.name = @"Mr.Peng";
person.run = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@ start running", weakSelf.name);
});
};
person.run()
输出:
(null)start running
为什么输出是这样的呢?
dispatch_after
这个函数里面。在run()
的block
结束之后,person
被自动释放了。又由于dispatch_after
里面捕获的__weak
的person,根据__weak
的实现原理,在原对象释放之后,__weak
对象就会变成null
,防止野指针。所以就输出了null
了。
那么怎么才能在weakSelf
之后,block
里面还能继续使用weakSelf
之后的对象呢?
究其根本原因就是weakSelf
之后,无法控制什么时候会被释放,为了保证在block
内不会被释放,需要添加__strong
。
在block
里面使用的__strong
修饰的weakSelf
是为了在函数生命周期中防止self
提前释放。strongSelf
是一个自动变量当block
执行完毕就会释放自动变量strongSelf
不会对self
进行一直进行强引用。
至此,我们就明白了
weakSelf
、strongSelf
的用途了。
weakSelf
是为了block
不持有self
,避免Retain Circle
循环引用。在Block
内如果需要访问self
的方法、变量,建议使用weakSelf
。
strongSelf
的目的是因为一旦进入block
执行,假设不允许self
在这个执行过程中释放,就需要加入strongSelf
。block
执行完后这个strongSelf
会自动释放,没有不会存在循环引用问题。如果在 Block 内需要多次 访问self
,则需要使用strongSelf
@weakify、@strongify实现原理
学习完了weakSelf
、strongSelf
之后,接下来再学习学习@weakify
、@strongify,这两个关键字是ARC
中避免Block
循环引用而开发的2个宏,这2个宏的实现过程很牛,值得我们学习。
@weakify
、@strongify
的作用和weakSelf
、strongSelf
对应的一样。这里我们具体看看大神是怎么实现这2个宏的。
直接从源码看起来。
#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
先从weakify(...)
开始。
#if DEBUG
#define rac_keywordify autoreleasepool {}
#else
#define rac_keywordify try {} @catch (...) {}
#endif
这里在debug
模式下使用@autoreleasepool
是为了维持编译器的分析能力,而使用@try/@catch
是为了防止插入一些不必要的autoreleasepool
。rac_keywordify
实际上就是autoreleasepool {}
的宏替换。因为有了autoreleasepool {}
的宏替换,所以weakify
要加上@,形成@autoreleasepool {}
。
#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
__VA_ARGS__
:总体来说就是将左边宏中 ... 的内容原样抄写在右边 __VA_ARGS__
所在的位置。它是一个可变参数的宏,是新的C99
规范中新增的,目前似乎只有gcc
支持(VC从VC2005开始支持)。
那么我们使用@weakify(self)
传入进去。__VA_ARGS__
相当于self。此时我们可以把最新开始的weakify
套下来。于是就变成了这样:
rac_weakify_,, __weak, __VA_ARGS__
整体替换MACRO, SEP, CONTEXT, ...
这里需要注意的是,源码中就是给的两个","逗号是连着的,所以我们也要等效替换参数,相当于SEP
是空值。
替换完成之后就是下面这个样子:
autoreleasepool {}
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(self))(rac_weakify_, , __weak, self)
现在我们需要弄懂的就是metamacro_concat
和 metamacro_argcount
是干什么用的。
继续看看metamacro_concat
的实现
#define metamacro_concat(A, B) \
metamacro_concat_(A, B)
#define metamacro_concat_(A, B) A ## B
##
是宏连接符。举个例子:
假设宏定义为#define XNAME(n) x##n
,代码为:XNAME(4)
,则在预编译时,宏发现XNAME(4)
与XNAME(n)
匹配,则令 n
为 4
,然后将右边的n的内容也变为4,然后将整个XNAME(4)
替换为 x##n
,亦即 x4
,故 最终结果为 XNAME(4)
变为 x4
。所以A##B
就是AB
。
metamacro_argcount
的实现
#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)
#define metamacro_at(N, ...) \
metamacro_concat(metamacro_at, N)(__VA_ARGS__)
metamacro_concat是上面讲过的连接符,那么metamacro_at, N = metamacro_atN,由于N = 20,于是metamacro_atN = metamacro_at20。
#define metamacro_at0(...) metamacro_head(__VA_ARGS__)
#define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__)
#define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__)
#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__)
metamacro_at20
的作用就是截取前20
个参数,剩下的参数传入metamacro_head
。
Objective-C
#define metamacro_head(...) \
metamacro_head_(__VA_ARGS__, 0)
#define metamacro_head_(FIRST, ...) FIRST
metamacro_head
的作用返回第一个参数。返回到上一级metamacro_at20
,如果我们从最源头的@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。
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(self)) = metamacro_concat(metamacro_foreach_cxt, 1) 最终可以替换成metamacro_foreach_cxt1。
在源码中继续搜寻。
// metamacro_foreach_cxt expansions
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
SEP \
MACRO(1, CONTEXT, _1)
#define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
SEP \
MACRO(2, CONTEXT, _2)
#define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \
SEP \
MACRO(3, CONTEXT, _3)
#define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \
SEP \
MACRO(4, CONTEXT, _4)
#define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \
SEP \
MACRO(5, CONTEXT, _5)
#define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \
SEP \
MACRO(6, CONTEXT, _6)
#define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \
SEP \
MACRO(7, CONTEXT, _7)
#define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \
SEP \
MACRO(8, CONTEXT, _8)
#define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \
SEP \
MACRO(9, CONTEXT, _9)
#define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
SEP \
MACRO(10, CONTEXT, _10)
#define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
SEP \
MACRO(11, CONTEXT, _11)
#define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
SEP \
MACRO(12, CONTEXT, _12)
#define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
SEP \
MACRO(13, CONTEXT, _13)
#define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
SEP \
MACRO(14, CONTEXT, _14)
#define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
SEP \
MACRO(15, CONTEXT, _15)
#define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
SEP \
MACRO(16, CONTEXT, _16)
#define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
SEP \
MACRO(17, CONTEXT, _17)
#define 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) \
metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
SEP \
MACRO(18, CONTEXT, _18)
#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)
metamacro_foreach_cxt这个宏定义有点像递归,这里可以看到N 最大就是20,于是metamacro_foreach_cxt19就是最大,metamacro_foreach_cxt19会生成rac_weakify_(0,__weak,_18),然后再把前18个数传入metamacro_foreach_cxt18,并生成rac_weakify_(0,__weak,_17),依次类推,一直递推到metamacro_foreach_cxt0。
#define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT)
metamacro_foreach_cxt0就是终止条件,不做任何操作了。
于是最初的@weakify
就被替换成
autoreleasepool {}
metamacro_foreach_cxt1(rac_weakify_, , __weak, self)
#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
代入参数
autoreleasepool {}
rac_weakify_(0,__weak,self)
最终需要解析的就是rac_weakify_
#define rac_weakify_(INDEX, CONTEXT, VAR) \
CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
把(0,__weak,self)的参数替换进来(INDEX, CONTEXT, VAR)。
INDEX = 0, CONTEXT = __weak,VAR = self,
于是
CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);
等效替换为
__weak __typeof__(self) self_weak_ = self;
最终@weakify(self) = __weak __typeof__(self) self_weak_ = self;
这里的self_weak_
就完全等价于我们之前写的weakSelf
。
- strongify
再继续分析strongify(...)
rac_keywordify
还是和weakify
一样,是autoreleasepool {}
,只为了前面能加上@
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
_Pragma("clang diagnostic pop")
strongify
比weakify
多了这些_Pragma
语句。
关键字_Pragma
是C99
里面引入的。_Pragma
比#pragma
(在设计上)更加合理,因而功能也有所增强。
上面的等效替换
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic pop
这里的clang语句的作用:忽略当一个局部变量或类型声明遮盖另一个变量的警告。
最初的
#define strongify(...) \
rac_keywordify \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
metamacro_foreach(rac_strongify_,, __VA_ARGS__) \
_Pragma("clang diagnostic pop")
strongify
里面需要弄清楚的就是metamacro_foreach
和 rac_strongify_
。
#define metamacro_foreach(MACRO, SEP, ...) \
metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
#define rac_strongify_(INDEX, VAR) \
__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
我们先替换一次,SEP
= 空 , MACRO
=rac_strongify_
, __VA_ARGS__
, 于是替换成这样。
metamacro_foreach_cxt(metamacro_foreach_iter,,rac_strongify_,self)
根据之前分析,metamacro_foreach_cxt
再次等效替换,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)
#define rac_strongify_(INDEX, VAR) \
__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
INDEX = 0, VAR = self,于是@strongify(self)就等价于
__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
等价于
__strong __typeof__(self) self = self_weak_;
注意@strongify(self)
只能使用在block
中,如果用在block
外面,会报错,因为这里会提示你Redefinition of 'self'
。
总结一下
@weakify(self) = @autoreleasepool{} __weak __typeof__ (self) self_weak_ = self;
@strongify(self) = @autoreleasepool{} __strong __typeof__(self) self = self_weak_;
经过分析以后,其实@weakify(self)
和 @strongify(self)
就是比我们日常写的weakSelf
、strongSelf
多了一个@autoreleasepool{}
而已,至于为何要用这些复杂的宏定义来做,目前还没有理解