您可以在Block Implementation Specification中找到详细信息。
用一个例子来解释是最容易的。考虑这个包含一个简单块的简单函数:
void outerFunction() {
int x = 7;
dispatch_block_t block = ^{
printf("%d\n", x);
};
dispatch_sync(dispatch_get_main_queue(), block);
}
请记住,这dispatch_block_t
是void (^)(void)
.
要编译该代码,编译器将首先创建两个结构定义:
struct Block_descriptor_1 {
unsigned long reserved;
unsigned long size;
const char *signature;
};
struct Block_literal_1 {
void *isa;
int flags;
int reserved;
void (*invoke)(void *);
struct Block_descriptor_1 *descriptor;
int x;
};
然后它创建一个 type 的全局变量Block_descriptor_1
,其中包含块的类型签名的大小Block_literal_1
和编码:
struct Block_descriptor_1 block_descriptor_1 = {
.size = sizeof(struct Block_literal_1),
.signature = "v4@?0"
};
它创建了一个包含块体的函数:
void outerFunction_block_invoke(void *voidLiteral) {
struct Block_literal_1 *literal = (struct Block_literal_1 *)voidLiteral;
printf("%d\n", literal->x);
}
请注意,块的主体已被重写,因此从封闭范围对变量x
的访问现在是对块文字成员的访问。
最后,编译器重写原始函数以在堆栈上创建一个块文字并使用该块文字的地址而不是块:
void outerFunction2() {
int x = 7;
struct Block_literal_1 block_literal_1 = {
.isa = __NSConcreteStackBlock,
.flags = BLOCK_HAS_SIGNATURE,
.invoke = outerFunction_block_invoke,
.descriptor = &block_descriptor_1,
.x = x
};
dispatch_sync(dispatch_get_main_queue(),
(__bridge dispatch_block_t)&block_literal_1);
}
请注意,块文字以指向特殊 Objective-C 的指针开头Class
。这允许将块文字视为一个 Objective-C 对象。
如果将__block
属性添加到局部变量x
,它会变得更加复杂。在这种情况下,编译器必须创建另一个结构来保存该变量以及有关变量的信息(例如,如果变量是指向对象的指针,如何保留和释放它)。这一切都在我在此答案顶部链接的规范中进行了解释。