//用__block修饰的本地变量b多了这个结构体
struct __Block_byref_b_0 { //直译为本地变量b在block中是通过引用的
void *__isa;
__Block_byref_b_0 *__forwarding;
int __flags;
int __size;
int b;
};
//此函数的作用是将栈上的b 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*/);
}
//此函数的作用是将栈上的b 释放
static void __main_block_dispose_0(struct __main_block_impl_0*src)
{
_Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static struct __main_block_desc_0 { //描述block的信息
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};
//可以看到b在block中是这个样子的
static int __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_b_0 *b = __cself->b; // bound by ref //这里是通过引用来修改的
......
}
struct __main_block_impl_0 { //需要填充的结构体
struct __block_impl impl;
struct __main_block_desc_0* Desc;
.....
//在要填充的结构体中
__Block_byref_b_0 *b; // by ref //多了一个属性
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_b_0 *_b, int flags=0) :b(_b->__forwarding) {
......
}
};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */
{ __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 20};
//可以看到被__block修饰的静态局部变量被转换成了这样
//__attribute__设置了一个block引用的属性
......
//填充此结构体
int (*block)(void) = ((int (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA,(__Block_byref_b_0 *)&b, 570425344));
......
//调用结构体
((int (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block)
......
}
return 0;
}
1、MRC情况下
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int b = 20;
NSLog(@"&b:%p ,b:%d",&b,b);
//&b:0x7ffeefbf7168 ,b:20
int (^block)(void) = ^(void)
{
NSLog(@"&b:%p ,b:%d",&b,b);
b = 100; //疑问 1:为什么能在block中修改__block修饰的本地变量b?
return b;
};
NSLog(@"&b:%p ,b:%d",&b,b);
//&b:0x7ffeefbf7168 ,b:20
NSLog(@"%d",block());
//&b:0x7ffeefbf7168 ,b:20
//100
//疑问 2:为什么在MRC中,被__block修饰的本地变量的地址在block内外都没有发生变化?
}
return 0;
}
2、MRC情况下,将block作copy
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int b = 20;
NSLog(@"&b:%p ,b:%d",&b,b);
//&b:0x7ffee2e79168 ,b:20
int (^block)(void) = [^(void)
{
NSLog(@"&b:%p ,b:%d",&b,b);
b = 100; //疑问 1:为什么能在block中修改__block修饰的本地变量b?
return b;
}copy]; //注意这里进行了copy
NSLog(@"&b:%p ,b:%d",&b,b);
//&b:0x60000003ce78 ,b:20
NSLog(@"%d",block());
//&b:0x60000003ce78 ,b:20
//100
//疑问 3:为什么在MRC中,将block作copy后的地址就改变了?
}
return 0;
}
3、ARC下
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int b = 20;
NSLog(@"&b:%p ,b:%d",&b,b);
//&b:0x7ffee2e79168 ,b:20
int (^block)(void) = ^(void)
{
NSLog(@"&b:%p ,b:%d",&b,b);
b = 100; //疑问 1:为什么能在block中修改__block修饰的本地变量b?
return b;
}; //注意这里没有进行copy
NSLog(@"&b:%p ,b:%d",&b,b);
//&b:0x60000003ce78 ,b:20
NSLog(@"%d",block());
//&b:0x60000003ce78 ,b:20
//100
//疑问 4:为什么在ARC中,将block不作copy,block内外的地址都能改变?
return 0;
}
疑问 1:为什么能在block中修改__block修饰的本地变量b?
- 因为b转换为__Block_byref_b_0结构体类型后,将地址传引用进去了。
疑问 2:为什么在MRC中,被__block修饰的本地变量b的地址在block内外都没有发生变化?
- 在1中,很明显此block是一个__NSStackBlock,而且b传的是地址进block,通过访问此地址输出的b的地址自然都是一样。
疑问 3:为什么在MRC中,将block作copy后的地址就改变了?
- **在2中,将__NSStackBlock进行copy后变成了一个__NSMallocBlock,多以对于__block修饰的本地变量b也copy了一份到堆上(通过__main_block_copy_0函数),而将原来的栈上的本地变量b释放了(通过__main_block_dispose_0()函数),得到的就是新的地址了。此过程发生在初始化block时候。
疑问 4:为什么在ARC中,将block不作copy,block内外的地址都能改变?
- 在3中,因为将block进行 = 操作了,所以在ARC中会导致调用objc_retainBlock->Block_copy->_Block_copy_internal方法链。并导致 __NSStackBlock_ 类型的 block 转换为 _NSMallocBlock_ 类型。自然也就得到疑问 3的答案了
总结:
MRC中:只有手动copy,block所捕获的东西才会复制到堆上,block也是__NSStackBlock类型的。__forwarding指针也只指向自己。
ARC中:只要将block进行 = ,block所捕获的东西就会复制到堆上,Block也是__NSMallocBlock类型的。相反它要是不 进行block = ,那么block也是__NSStackBlock类型的。