Block如何捕获其封闭范围之外的变量?

iCa*_*arp 14 objective-c objective-c-blocks

我知道Objective-C Block可以捕获并设置其封闭范围之外的变量值.它是如何做到的?

Jos*_*ell 25

它实际上相当简单,并在Clang的Block Implementation Spec中的"Imported Variables"部分中进行了描述.

当编译器遇到像这样的块时:

^{ if( numBalloons > numClowns) abort(); }
Run Code Online (Sandbox Code Playgroud)

它创建了一个文字结构,其中包括两个在这里很重要的元素.有一个指向块中可执行代码的函数指针,以及一个const在块内引用的每个变量的字段.像这样的东西:

struct __block_literal_1 {
    /* other fields */
    void (*invoke)(struct __block_literal_1 *);
    /* ... */
    const int numBalloons;
    const int numClowns;
};
Run Code Online (Sandbox Code Playgroud)

请注意,该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. *
 };
Run Code Online (Sandbox Code Playgroud)

然后,在invoke函数内部,对捕获的变量的引用就像结构的任何其他成员一样the_block->numBalloons.

对象类型变量的情况稍微复杂一些,但同样的原则也适用.


jse*_*ano 6

在块对象的代码体内,可以用五种不同的方式处理变量.

您可以引用三种标准类型的变量,就像在函数中一样:

  • 全局变量,包括静态局部变量
  • 全局函数(不是技术变量)
  • 来自封闭范围的局部变量和参数

Blocks还支持另外两种类型的变量:

  1. 在功能级别是__block变量.这些在块(以及封闭范围)内是可变的,并且如果将任何引用块复制到堆中,则会保留它们.

  2. const 进口.

最后,在方法实现中,块可以引用Objective-C实例变量 - 请参阅对象和块变量.

以下规则适用于块中使用的变量:

  1. 可以访问全局变量,包括封闭词法范围内存在的静态变量.

  2. 传递给块的参数是可访问的(就像函数的参数一样).

  3. 封闭的词法范围本地的堆栈(非静态)变量被捕获为const变量.

    它们的值是在程序中的块表达点处获取的.在嵌套块中,从最近的封闭范围捕获值.

  4. 使用__block存储修饰符声明的封闭词法范围的局部变量由引用提供,因此是可变的.

    任何更改都会反映在封闭的词法范围中,包括在相同的封闭词法范围内定义的任何其他块.这些在__block存储类型中有更详细的讨论.

  5. 在块的词法范围内声明的局部变量,其行为与函数中的局部变量完全相同.

每次调用块都会提供该变量的新副本.这些变量又可以const在块中包含的块中用作或引用变量.

从这里:http:
//developer.apple.com/library/ios/#documentation/cocoa/conceptual/Blocks/Articles/bxVariables.html

  • 还有一个......在块的范围内引用的实例变量意味着对"self"的强引用,而实例变量将反映块执行时存在的任何值.这可能令人困惑. (4认同)
  • 该问题询问块如何设法捕获变量。不是捕获变量的语义。 (2认同)