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

时间:2013-07-23 15:01:00

标签: objective-c objective-c-blocks

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

3 个答案:

答案 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还支持另外两种类型的变量:

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

  2.   
  3. const进口。

  4.         

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

         

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

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

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

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

           

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

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

           

      任何更改都会反映在封闭的词法范围中,包括在相同的词法范围内定义的任何其他块。这些将在__block Storage Type。

    8. 中详细讨论   
    9. 在块的词法范围内声明的局部变量,其行为与函数中的局部变量完全相同。

    10.         

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

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

答案 2 :(得分:0)

基本上,块“对象”包含块对象内的变量(就像块对象的“实例变量”),用于每个捕获的局部变量。 (Josh Caswell的回答提供了有关如何实现它的更多细节。)创建块时,此时每个捕获的局部变量的值将被复制到块内的相应变量中。每当在块内部使用变量时,它就会在块内部使用此变量。