网上有很多讲block底层实现的文章,里面会讲到捕获了auto修饰的局部变量的block,在MRC下是栈block,在ARC下是堆block,这篇文章只想讨论一下为什么ARC下,捕获了auto修饰的局部变量的block,系统为什么要将block从栈上拷贝到堆上。
block的底层实现
void (^resultBlock) (void);
resultBlock = ^ {
NSLog(@"=======");
};
resultBlock();
复制代码
将.m文件转成.cpp文件,查看底层实现。
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;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_zj_qlh1_2756d7_hcsp3s_fnrmr0000gn_T_main_accc59_mi_0);
}
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;
void (*resultBlock) (void);
resultBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)resultBlock)->FuncPtr)((__block_impl *)resultBlock);
}
return 0;
}
复制代码
- void (*resultBlock) (void)
先定义了一个指针变量 resultBlock (函数指针)
复制代码
- resultBlock = ((void (*)())&__main_block_impl_0
__main_block_impl_0 是c++的构造函数,传入__main_block_func_0 和 &__main_block_desc_0_DATAl两个参数。
返回构建好的结构体变量的地址,并赋值给resultBlock
复制代码
总结一下,其实block的底层实现就是一个结构体。
一般来讲,方法中的局部变量(非调用类似alloc方法创建的)是在栈上的,方法调用完毕,系统会自动回收栈内存。
以下demo运行在模拟器上。
内存分段不同,地址区别较大。
局部变量a 地址为0x7ffee41e0ebc,内存分配在栈上,地址一般比较大
全局变量b 地址为0x10c79f168,内存在静态区
局部变量obj分配在栈区,地址为0x7ffee41e0eb0,该地址中存储的值为alloc方式创建出来的堆区变量0x600000ba4210
复制代码
再看下面这段代码:
定义一个全局区的指针变量p,在viewDidLoad中给p赋值,此时p中存储的是局部变量a的地址,a的地址是在栈上开辟的,当方法调用完毕,栈空间中的值会释放,此时如果在touchesBegan方法中访问p中的值会出现什么问题?
a的地址在栈上 : 0x7ffee2fc0eac 该地址中存储的值为10
将a的地址赋值给指针p所指向的全局区的内存空间
*p的值为10
*p的地址就是a在栈上的地址
当viewdidload 方法调用完成后,系统会释放栈空间的值,地址0x7ffeee6e5eac中存储的10,就会被释放
当在其他方法中继续访问0x7ffee2fc0eac中的值时,就会出现垃圾值32767
复制代码
同理:
resultBlock 是定义在全局区的变量
tempBlock 地址是在栈区
将tempBlock 地址中的值赋值给全局区resultBlock的地址中
复制代码
验证一下:
tempBlock 的地址 0x7ffee8657ea0
resultBlock 的地址 0x108330d20
tempBlock 和 resultBlock 中都是存储的堆block <NSMallocBlock: 0x600001fec6f0>,
本来tempBlock 是在栈上创建的,此时打印出来的地址中的值却是在堆上,说明系统帮我们做了copy操作,主要是为了延长该block生命,避免在其他方法中访问时,变量a出现垃圾值。
此时点击屏幕,调用resultBlock方法,就能正常打印a的值
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END