首页 > 解决方案 > 为什么 Block 使用 struct __Block_byref_object_0 而不是单个指针来捕获用“__block”修饰的变量

问题描述

这是我在文件 main.m 中的源代码

 __block NSInteger blockInteger = 123;
 static NSInteger staticInteger = 123;
 void (^testBlock)(void) = ^() {
     blockInteger++;
     staticInteger++;
     NSLog(@"%ld", blockInteger);
     NSLog(@"%ld", staticInteger);
 };
 testBlock();

当我使用 clang 命令“clang -rewrite-objc main.m”时,我得到了这个

struct __Block_byref_blockInteger_0 {
   void *__isa;
   __Block_byref_blockInteger_0 *__forwarding;
   int __flags;
   int __size;
   NSInteger blockInteger;
};
struct __main_block_impl_0 {
   struct __block_impl impl;
   struct __main_block_desc_0* Desc;
   NSInteger *staticInteger;
   __Block_byref_blockInteger_0 *blockInteger; // by ref
   ...
};

我想知道为什么 block 使用 __Block_byref_blockInteger_0 来捕获 blockInteger 因为它使用 NSInteger 指针来捕获静态变量。__Block_byref_blockInteger_0 究竟做了什么?通过与指针比较,这个结构有什么优势?

标签: objective-cclangobjective-c-blocks

解决方案


编译器正在创建许多结构来帮助块引用其“封闭”值。(请记住,块会复制或“包含”块外的所有值,这就是为什么块也称为“闭包”的原因。)

所以第一个结构(__Block_byref_blockInteger_0)是创建一个对象来封装blockInteger自动。这是因为自动变量在函数结束时消失了,但块必须能够在很久之后引用它们。

第二个结构封装__Block_byref_blockInteger_0了块“捕获”的所有值(包括 )。这为块提供了对其所有封闭值的单一引用,这些值在创建块时复制。

现在NSInteger *staticInteger实例值有点奇怪,因为全局地址staticInteger不能改变。但这只是一个很小的区别,因为它仍然只是地址的副本;该地址是否可以更改并不重要。

我怀疑这是因为名称范围;在函数内声明的静态符号范围仅限于该函数。如果您查看编译器输出,您会看到您声明的每个块都会创建一个不可见的静态函数来包含其代码。由于第二个静态函数通常无法引用在另一个函数中声明的静态,因此复制静态地址是块函数访问它的唯一方法。


推荐阅读