为什么 Block 使用结构__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_byref_blockInteger_0来捕获blockInteger,因为它使用NSInteger指针来捕获静态变量。__Block_byref_blockInteger_0到底做什么?与指针相比,这个结构有什么优点?

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

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

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

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

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

最新更新