返回
深入解析 Block 截获对象:栈和堆的奥秘
IOS
2024-01-22 17:28:01
在上一篇文章中,我们深入探究了 __block 变量的存储域。这一次,让我们将目光投向 Block 如何截获对象,揭开栈和堆之间的奥秘。
栈 Block 截获对象
首先,我们考察栈 Block 截获对象的情况。在 __main_block_impl_0 结构体中,成员 NSMutableArray *__strong arrM;
强引用了 arrM 变量。这意味着,如果 arrM 变量在 Block 外部被释放,那么它在 Block 内部仍然可以访问。
void (^block)(void) = ^{
__strong NSMutableArray *arrM = [NSMutableArray array];
// ...
};
在这种情况下,arrM 变量会在 Block 执行后立即被释放,但它仍然可以通过 __strong 指针在 Block 内部访问。
堆 Block 截获对象
当 Block 被分配到堆上时,情况就变得更加复杂。堆 Block 使用 _NSConcreteStackBlock 结构体,该结构体包含一个指向 __block_impl 结构体的指针,而 __block_impl 结构体又包含一个指向被截获对象的指针。
_NSConcreteStackBlock *block = (_NSConcreteStackBlock *)malloc(sizeof(_NSConcreteStackBlock));
block->ivar_list = (__block_impl *)malloc(sizeof(__block_impl));
block->ivar_list->object = arrM;
当堆 Block 被释放时,它会释放指向 __block_impl 结构体的指针,但它不会释放 __block_impl 结构体本身或被截获的对象。这意味着,被截获的对象仍可能被外部代码访问,从而导致野指针问题。
避免野指针问题
为了避免野指针问题,需要确保在 Block 销毁之前释放被截获的对象。可以通过在 Block 内部使用 __weak 引用或在外部代码中明确释放被截获的对象来实现。
void (^block)(void) = ^{
__weak NSMutableArray *arrM = [NSMutableArray array];
// ...
};
结论
理解 Block 如何截获对象对于编写健壮且高效的代码至关重要。通过了解栈和堆 Block 之间的区别以及避免野指针问题的技术,我们可以有效利用 Block 的强大功能,而无需担心意外的行为。