Block
一、什么是block
1、block是什么
下面是一个简单的block:
int main(int argc, const char * argv[]) {
@autoreleasepool {
^{printf("这是一个block");}();
}
return 0;
}
对其执行clang -rewrite-objc编译转换成C++实现,得到以下代码:
struct __block_impl {
void *isa; //指向所属类(即block类型)的指针,isa指针说明block也是对象
int Flags;
int Reserved;
void *FuncPtr; //block执行时调用的函数指针,block函数的地址。存储着 __main_block_func_0 函数的地址
};
// main函数中第0个block,即上面代码中的block:^{printf("这是一个block");}();
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//结构体的构造函数
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block块函数体的定义部分,可以看出block的代码块都转化为了普通的函数,并且函数会默认增加一个隐藏的__cself参数,用来指向block对象本身。
//block代码块中的代码被封装成 __main_block_func_0 函数,FuncPtr则存储着 __main_block_func_0 函数的地址
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("这是一个block");}
static struct __main_block_desc_0 {
size_t reserved; //保留字段
size_t Block_size; //block大小(sizeof(struct __main_block_impl_0))
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//__main_block_impl_0的FuncPtr指向了函数__main_block_func_0
//__main_block_impl_0的Desc也指向了定义__main_block_desc_0时就创建的__main_block_desc_0_DATA,其中纪录了block结构体大小等信息
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA))();
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
2、block的实际结构
Block_private.h文件中对block的相关结构体的真实定义:
/* Revised new layout. */
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src); //辅助拷贝函数,处理block范围外的变量时使用
void (*dispose)(void *); //辅助销毁函数,处理block范围外的变量时使用
};
struct Block_layout {
void *isa; //isa指针,所有对象都有该指针,用于实现对象相关的功能。
int flags;
int reserved; //reserved,保留变量
void (*invoke)(void *, ...); //函数指针,指向具体的block实现的函数调用地址。block定义时内部的执行代码都在这个函数中
struct Block_descriptor *descriptor; //block的详细描述
/* Imported variables. */
};
block的结构如下图:
二、block的类型
block有三种类型:_NSConcreteGlobalBlock(全局)、_NSConcreteStackBlock(栈)和_NSConcreteMallocBlock(堆)。其中_NSConcreteGlobalBlock和_NSConcreteStackBlock可以在代码中创建。下面代码中创建了一个global block和一个stack block。
typedef void(^SomeBlock)();
//global block
void (^globalBlock)(void) = ^{ printf("全局block"); };
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
void (^aBlock)(void) = ^{
printf("a = %d", a);
};
//
void (^bBlock)() = ^{};
//
SomeBlock cBlock = ^{};
NSLog(@"globalBlock=%@\n,aBlock=%@\n,bBlock=%@\n,cBlock=%@\n", globalBlock,aBlock, bBlock, cBlock);
}
return 0;
}
运行结果
2019-03-19 11:46:26.388349+0800 BlockTest[90500:9974201]
globalBlock=<__NSGlobalBlock__: 0x1000020b8>,
aBlock=<__NSStackBlock__: 0x7ffeefbff4d8>,
bBlock=<__NSGlobalBlock__: 0x100002118>,
cBlock=<__NSGlobalBlock__: 0x100002158>
Program ended with exit code: 0
如何判断block是哪种类型?
(1)没有访问auto变量的block是NSGlobalBlock ,放在数据段;
(2)访问了auto变量的block是NSStackBlock;
(3)[NSStackBlock copy]操作就变成了NSMallocBlock。
NSConcreteMallocBlock类型的block通常不会在源码中直接出现,因为默认它是当一个block被copy的时候,才会将这个block复制到堆中。由于block的拷贝最终都会调用_Block_copy_internal函数(runtime.c中的_Block_copy_internal函数),所以观察这个函数就可以知道堆中block是如何被创建的:
static void *_Block_copy_internal(const void *arg, const bool wantsOne) {
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GC) {
// GC refcounting is expensive so do most refcounting here.
if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 2)) {
// Tell collector to hang on this - it will bump the GC refcount version
_Block_setHasRefcount(aBlock, true);
}
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
// Its a stack block. Make a copy.
if (!isGC) {
// 申请block的堆内存
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return NULL;
// 拷贝栈中block到刚申请的堆内存中
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
// 改变isa指向_NSConcreteMallocBlock,即堆block类型
result->isa = _NSConcreteMallocBlock;
_Block_call_copy_helper(result, aBlock);
return result;
}
else {
//省略...
}
}
三、block与变量
1、block与基本数据类型变量
void testBlockVar(void);
int c = 30; //全局变量
static int d = 40; //全局静态变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
testBlockVar();
}
return 0;
}
void testBlockVar() {
int a = 10; //局部变量
static int b = 20; //静态局部变量
void (^someBlock)(void) = ^(void) {
b = 2222;
c = 33;
d = 44;
NSLog(@"a = %d, b = %d, c = %d, d = %d\n", a, b, c, d);
};
a = 11;
b = 22;
someBlock();
}
上面代码运行结果a = 10, b = 2222, c = 33, d = 44。编译之后:
void testBlockVar(void);
//全局变量c和全局静态变量d存储在静态数据存储区,在程序结束前不会被销毁,所以block直接访问了对应的变量,因此在结构体__testBlockVar_block_impl_0中没有拷贝
int c = 30;
static int d = 40;
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
testBlockVar();
}
return 0;
}
//全局变量c和全局静态变量d存储在静态数据存储区,在程序结束前不会被销毁,所以block直接访问了对应的变量,因此在结构体__testBlockVar_block_impl_0中没有拷贝
struct __testBlockVar_block_impl_0 {
struct __block_impl impl;
struct __testBlockVar_block_desc_0* Desc;
int *b; //指针传递。static修饰的静态局部变量b传递到block内部的是指针,在 __testBlockVar_block_func_0 函数内部就可以拿到b的内存地址,因此就可以在block内部修改b的值。
int a; //值传递
__testBlockVar_block_impl_0(void *fp, struct __testBlockVar_block_desc_0 *desc, int *_b, int _a, int flags=0) : b(_b), a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __testBlockVar_block_func_0(struct __testBlockVar_block_impl_0 *__cself) {
int *b = __cself->b; // bound by copy
int a = __cself->a; // bound by copy
(*b) = 2222;
c = 33;
d = 44;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fb_61h7kdld7qz8hz9ph2rt22k00000gn_T_main_ccbce4_mi_0, a, (*b), c, d);
}
void testBlockVar() {
int a = 10;
static int b = 20;
void (*someBlock)(void) = ((void (*)())&__testBlockVar_block_impl_0((void *)__testBlockVar_block_func_0, &__testBlockVar_block_desc_0_DATA, &b, a));
a = 11;
b = 22;
((void (*)(__block_impl *))((__block_impl *)someBlock)->FuncPtr)((__block_impl *)someBlock);
}
查看编译后的代码可以得出结论:
(1)局部变量:所有在block代码块引用的局部变量都会成为结构体的同名数据成员,因此struct __testBlockVar_block_impl_0结构体增加了名为int a的成员变量。
(2)静态局部变量:static修饰的静态局部变量b传递到block内部的是指针,在 __testBlockVar_block_func_0 函数内部就可以拿到b的内存地址,因此就可以在block内部修改b的值。
(3)全局变量和全局静态全局变量:全局变量c和全局静态变量d存储在静态数据存储区,在程序结束前不会被销毁,所以block直接可以访问对应的变量,因此在结构体__testBlockVar_block_impl_0中没有也不需要拷贝变量作为结构体成员变量。如下图:
2、block与__strong和__weak变量
//main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [Person new];
p.name = @"胖虎の朋友";
someBlock = ^{
NSLog(@"---block内部:%@", p.name);
};
someBlock();
NSLog(@"======");
}
return 0;
}
//Person.m
- (void)dealloc
{
NSLog(@"Person dealloc");
}
运行结果:
2019-03-15 16:39:18.747356+0800 BlockTest[14917:6258900] ---block内部:胖虎の朋友
2019-03-15 16:39:18.747505+0800 BlockTest[14917:6258900] ======
Program ended with exit code: 0
大括号执⾏完毕之后,p依然不不会被释放。p为auto变量,即block有一个强引⽤指向p,所以block不被销毁的话,p也不会销毁。所以Person类的dealloc没有执行。查看编译后代码:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong p; //强引用p
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _p, int flags=0) : p(_p) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
如果为在block内部调用的p变量添加__weak修饰符:
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [Person new];
p.name = @"胖虎の朋友";
__weak Person *weakP = p;
someBlock = ^{
NSLog(@"---block内部:%@", weakP.name);
};
someBlock();
NSLog(@"======");
}
return 0;
}
运行结果:
2019-03-15 16:41:20.836909+0800 BlockTest[14952:6266465] ---block内部:胖虎の朋友
2019-03-15 16:41:20.837082+0800 BlockTest[14952:6266465] ======
2019-03-15 16:41:20.837098+0800 BlockTest[14952:6266465] Person dealloc
Program ended with exit code: 0
编译之后:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__weak weakP; //weak p
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakP, int flags=0) : weakP(_weakP) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
3、block与__block修饰的变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
testBlock();
}
return 0;
}
void testBlock()
{
//下面分别定义各种类型的变量
__block int b = 20; //带__block修饰符的block普通变量
NSString *str = @"123";
__block NSString *blockStr = str; //带__block修饰符的block OC变量
//定义一个block块并带一个参数
void (^someBlock)(void) = ^{
NSLog(@"b=%d, str=%@, blockStr=%@", b, str, blockStr);
};
b = 40; //修改值会影响someBlock内的计算结果
str = @"str"; //修改值不会影响someBlock内的计算结果
blockStr = @"blockStr"; //修改值会影响someBlock内的计算结果
someBlock(); //执行block代码
}
上面代码的运行结果:b=40, str=123, blockStr=blockStr。由此可以看出只有带有__block修饰符的变量b和blockStr被修改后影响了block中的值。将代码编译后:
//__block修饰的变量b变成了结构体__Block_byref_b_0,内存结构和OC类兼容
struct __Block_byref_b_0 {
void *__isa;
__Block_byref_b_0 *__forwarding;//__forwarding是指向自己在堆中的地址,访问时通过b->__forwarding->b保证操作的是堆中的变量b
int __flags;
int __size;
int b;//保存定义的变量b
};
//__block修饰的变量blockStr变成了结构体__Block_byref_blockStr_1
struct __Block_byref_blockStr_1 {
void *__isa;
__Block_byref_blockStr_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSString *__strong blockStr;
};
struct __testBlock_block_impl_0 {
struct __block_impl impl;
struct __testBlock_block_desc_0* Desc;
//所有在block代码块引用的外部数据都会成为结构体的同名数据成员
NSString *__strong str;
__Block_byref_b_0 *b; // by ref
__Block_byref_blockStr_1 *blockStr; // by ref
//结构体的构造函数
__testBlock_block_impl_0(void *fp, struct __testBlock_block_desc_0 *desc, NSString *__strong _str, __Block_byref_b_0 *_b, __Block_byref_blockStr_1 *_blockStr, int flags=0) : str(_str), b(_b->__forwarding), blockStr(_blockStr->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __testBlock_block_func_0(struct __testBlock_block_impl_0 *__cself) {
__Block_byref_b_0 *b = __cself->b; // bound by ref
__Block_byref_blockStr_1 *blockStr = __cself->blockStr; // bound by ref
NSString *__strong str = __cself->str; // bound by copy
////b->__forwarding->b用来保证操作的始终是堆中的拷贝b,而不是栈中的b
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fb_61h7kdld7qz8hz9ph2rt22k00000gn_T_main_c2a8d8_mi_1, (b->__forwarding->b), str, (blockStr->__forwarding->blockStr));
}
void testBlock()
{
//由__block修饰的变量b变成了__Block_byref_b_0对象
__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 20};
NSString *str = (NSString *)&__NSConstantStringImpl__var_folders_fb_61h7kdld7qz8hz9ph2rt22k00000gn_T_main_c2a8d8_mi_0;
//由__block修饰的blockStr页变成了__Block_byref_blockStr_1对象
__attribute__((__blocks__(byref))) __Block_byref_blockStr_1 blockStr = {(void*)0,(__Block_byref_blockStr_1 *)&blockStr, 33554432, sizeof(__Block_byref_blockStr_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, str};
void (*someBlock)(void) = ((void (*)())&__testBlock_block_impl_0((void *)__testBlock_block_func_0, &__testBlock_block_desc_0_DATA, str, (__Block_byref_b_0 *)&b, (__Block_byref_blockStr_1 *)&blockStr, 570425344));
}
结构体__Block_byref_b_0中__Block_byref_b_0 *__forwarding:指向自己在堆中的地址。由此可以保证只要是使用b->_forwarding->b访问的都是堆中的变量b。如下图所示:
四、block辅助函数
主要指的是copy和dispose辅助函数,负责block的拷贝和释放。
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
__block int b = 20; //block修饰的基本数据类型
NSString *c = @"c";
someBlock = ^{
NSLog(@"a=%d, b=%d, c=%@", a, b, c);
};
someBlock();
}
return 0;
}
在捕获变量为__block修饰的基本类型,或者为对象时,block才会有这两个辅助函数。编译之后:
//Step 3(copy操作):_Block_object_assign函数内部会根据变量在__main_block_impl_0中的修饰符进行引用计数器的操作。如果为strong计数器+1,为weak则不变
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
NSString *__strong c;
__Block_byref_b_0 *b; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, NSString *__strong _c, __Block_byref_b_0 *_b, int flags=0) : a(_a), c(_c), b(_b->__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_b_0 *b = __cself->b; // bound by ref
int a = __cself->a; // bound by copy
NSString *__strong c = __cself->c; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fb_61h7kdld7qz8hz9ph2rt22k00000gn_T_main_4bb492_mi_1, a, (b->__forwarding->b), c);
}
//Step 2(copy操作):调用_Block_object_assign函数
//copy辅助函数
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_assign((void*)&dst->c, (void*)src->c, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
//dispose辅助函数
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->c, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
//Step 1(copy操作):调用__main_block_desc_0中的__main_block_copy_0函数
//Step 1(dispose操作):调用__main_block_desc_0中的__main_block_dispose_0函数;
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};
当对block进行copy操作时,主要执行如下操作:
(1)调用__main_block_desc_0中的__main_block_copy_0函数;
(2)__main_block_copy_0函数内部会调用_Block_object_assign函数;
(3)_Block_object_assign函数内部根据变量在__main_block_impl_0中的修饰符进行引用计数器的操作,如果为strong计数器+1,为weak则不变。
当block从堆中移除时:_Block_object_dispose会断开对对象的引用,而对象是否被释放取决于对象自己的引用计数。
(1)自动调用__main_block_desc_0中的__main_block_dispose_0函数;
(2)__main_block_dispose_0函数内部会调用_Block_object_dispose函数。
五、block与循环引用
1、场景一:
//main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person new];
person.name = @"啊哈哈";
person.block = ^{
NSLog(@"---%@", person.name);
};
person.block();
}
return 0;
}
//Person.h
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) void(^block)(void);
@end
//Person.m
@implementation Person
- (void)dealloc
{
NSLog(@"Person dealloc");
}
@end
执行结果:2019-03-15 18:27:15.358609+0800 BlockTest[16449:6652153] ---啊哈哈
person没有被释放,产生了循环引用。其原因如下图所示:
稍作修改之后:
//main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person new];
person.name = @"啊哈哈";
__weak Person *wp = person;
person.block = ^{
NSLog(@"---%@", wp.name);
};
person.block();
}
return 0;
}
//打印结果:
2019-03-15 18:32:37.898324+0800 BlockTest[16538:6672390] ---啊哈哈
2019-03-15 18:32:37.898488+0800 BlockTest[16538:6672390] Person dealloc
Program ended with exit code: 0
2、场景二:
//main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.block();
}
return 0;
}
//Person.m
- (instancetype)init
{
self = [super init];
if (self) {
self.block = ^{
NSLog(@"===%@",[self class]);
};
}
return self;
}
- (void)dealloc
{
NSLog(@"Person dealloc");
}
//运行结果:
2019-03-15 18:38:39.059249+0800 BlockTest[16718:6698586] ===Person
Program ended with exit code: 0
编译Person.m文件:
struct __Person__init_block_impl_0 {
struct __block_impl impl;
struct __Person__init_block_desc_0* Desc;
Person *__strong self;
__Person__init_block_impl_0(void *fp, struct __Person__init_block_desc_0 *desc, Person *__strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
这⾥可以看到 __Person__init_block_impl_0 结构体中创建了一个Person *__strong self的强指针指向init方法中self 指针所指向的person对象,使person引⽤计数+1,而person对block也有⼀个强引用。这⾥就造成了循环引⽤用。
原因:之前说过block会捕获局部变量,上⾯的OC函数调用转化为runtime代码为 objc_msgSend(self, @selector(init)) 在OC的方法中有2个隐藏参数self和cmd,这2个参数作为函数的形参在⽅法作⽤域中属于局部变量, 所以在block中使用self就满足之前提到的block会捕获局部变量。解决方案如下:
//Person.m
- (instancetype)init
{
self = [super init];
if (self) {
__weak typeof(self) weakself = self;
self.block = ^{
NSLog(@"===%@\n",[weakself class]);
};
}
return self;
}
运行结果:
2019-03-15 18:55:48.421348+0800 BlockTest[17081:6778502] ===Person
2019-03-15 18:55:48.421693+0800 BlockTest[17081:6778502] Person dealloc
Program ended with exit code: 0
编译Person.m:
struct __Person__init_block_impl_0 {
struct __block_impl impl;
struct __Person__init_block_desc_0* Desc;
Person *__weak weakself;
__Person__init_block_impl_0(void *fp, struct __Person__init_block_desc_0 *desc, Person *__weak _weakself, int flags=0) : weakself(_weakself) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
3、场景三:
在block中调⽤用super也会造成循环引用,如下:
//main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.block();
}
return 0;
}
//Person.m
- (instancetype)init
{
self = [super init];
if (self) {
self.block = ^{
[super init];
};
}
return self;
}
- (void)dealloc
{
NSLog(@"Person dealloc");
}
编译后:
//当使⽤[self class]时,会调用objc_msgSend函数,第一个参数receiver就是self,⽽第二个参数,要先找到self所在的这个class的⽅法列表
//当使用[super class]时,会调用objcmsgSendSuper函数,此时会先构造一个 __rw_objc_super 的结构体作为objcmsgSendSuper的第⼀个参数。该结构体第一个成员变量receiver仍然是self,而第二个成员变量super_class即是所在类的⽗类
static void __Person__init_block_func_0(struct __Person__init_block_impl_0 *__cself)
{
((Person *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){
(id)self,
(id)class_getSuperclass(objc_getClass("Person"))
},
sel_registerName("init"));
}
//
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
//runtime对外暴暴露露的类型为:
//结构体第一个成员receiver代表方法的接收者,第二个成员super_class代表方法接收者的父类
struct objc_super {
__attribute__((objc_ownership(none))) _Nonnull id receiver;
__attribute__((objc_ownership(none))) _Nonnull Class super_class;
};
因此:
self.block = ^{
[super init];
};
转换为源码是:
self.block = ^{
struct objc_super superInfo = {
.receiver = self,
.super_class = class_getSuperclass(objc_getClass("Person")),
};
((Class(*)(struct objc_super *, SEL))objc_msgSendSuper)(&superInfo,@selector(init));
};
可以明显看到,block强引用了self,而self也强引用了block。
解决方案:
__weak __typeof(self) weakSelf = self;
self.block = ^{
struct objc_super superInfo = {
.receiver = weakSelf,
.super_class = class_getSuperclass(NSClassFromString(@"Person")),
};
((Class(*)(struct objc_super *, SEL))objc_msgSendSuper)(&superInfo,@selector(class));
};
上面代码编译后,self已经变成了weakSelf:
static void __Person__init_block_func_0(struct __Person__init_block_impl_0 *__cself) {
Person *__weak weakSelf = __cself->weakSelf; // bound by cop
struct objc_super superInfo = {
.receiver = weakSelf,
.super_class = class_getSuperclass(NSClassFromString((NSString *)&__NSConstantStringImpl__var_folders_fb_61h7kdld7qz8hz9ph2rt22k00000gn_T_Person_d2ee36_mi_0))
};
((Class(*)(struct objc_super *, SEL))objc_msgSendSuper)(&superInfo,sel_registerName("class"));
}
参考资料:
https://opensource.apple.com/source/libclosure/libclosure-65/Block_private.h.auto.html
https://opensource.apple.com/source/libclosure/libclosure-65/runtime.c.auto.html
http://blog.devtang.com/2013/07/28/a-look-inside-blocks/