我知道Objective-C Block可以捕获并设置其封闭范围之外的变量值。它是如何做到的?
答案 0 :(得分:24)
实际上相当简单,并在"Imported Variables"部分的Clang的块实现规范中进行了描述。
当编译器遇到像:
这样的块时^{ if( numBalloons > numClowns) abort(); }
它创建了一个文字结构,其中包括 - 其中包括两个在这里很重要的元素。有一个指向块中可执行代码的函数指针,以及一个const
字段,用于在块内引用的每个变量。像这样:
struct __block_literal_1 {
/* other fields */
void (*invoke)(struct __block_literal_1 *);
/* ... */
const int numBalloons;
const int numClowns;
};
请注意,invoke
函数将获取指向此处正在定义的类型的结构的指针;也就是说,Block在执行代码时会自行传递。因此,代码可以访问结构的成员。
在声明之后,编译器创建了Block的定义,它只是使用引用的变量来初始化struct
中的正确字段:
struct __block_literal_1 __block_literal_1 = {
/* Other fields */
__block_invoke_2, /* This function was also created by the compiler. */
/* ... */
numBalloons, /* These two are the exact same variables as */
numClowns /* those referred to in the Block literal that you wrote. *
};
然后,在invoke
函数内,对捕获变量的引用就像结构的任何其他成员the_block->numBalloons
一样。
对象类型变量的情况稍微复杂一些,但同样的原则适用。
答案 1 :(得分:6)
在块对象的代码体中,变量可以用五种不同的方式处理。
您可以引用三种标准类型的变量,就像使用函数一样:
- 全局变量,包括静态本地
- 全局函数(不是技术变量)
- 来自封闭范围的局部变量和参数
Blocks还支持另外两种类型的变量:
在功能级别是
__block
个变量。这些在块(以及封闭范围)内是可变的,并且如果将任何引用块复制到堆中,则会保留它们。- 醇>
const
进口。最后,在方法实现中,块可以引用Objective-C实例变量 - 请参阅对象和块变量。
以下规则适用于块中使用的变量:
可以访问全局变量,包括封闭词法范围内的静态变量。
可以访问传递给块的参数(就像函数的参数一样)。
封闭词法范围本地的堆栈(非静态)变量被捕获为
const
个变量。它们的值是在程序中的块表达式处获取的。在嵌套块中,从最近的封闭范围捕获值。
- 中详细讨论
使用
__block
存储修饰符声明的封闭词法范围的局部变量由引用提供,因此是可变的。任何更改都会反映在封闭的词法范围中,包括在相同的词法范围内定义的任何其他块。这些将在__block Storage Type。
- 醇>
在块的词法范围内声明的局部变量,其行为与函数中的局部变量完全相同。
每次调用块都会提供该变量的新副本。这些变量又可以用作块中包含的块中的
const
或引用变量。
答案 2 :(得分:0)
基本上,块“对象”包含块对象内的变量(就像块对象的“实例变量”),用于每个捕获的局部变量。 (Josh Caswell的回答提供了有关如何实现它的更多细节。)创建块时,此时每个捕获的局部变量的值将被复制到块内的相应变量中。每当在块内部使用变量时,它就会在块内部使用此变量。