返回

深入解析 Block 截获对象:栈和堆的奥秘

IOS

在上一篇文章中,我们深入探究了 __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 的强大功能,而无需担心意外的行为。