2013-07-23 80 views
10

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

+1

我认为它基本上对待它,就好像外部变量不超出范围时,他们被定义的范围结束,使用块很多,但我不会算作自己的专家 – Fonix

回答

5

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

您可以参考三个标准类型的变量,就像你从一个函数将:

  • 全局变量,包括静态当地人
  • 全局函数(这在技术上不变量)
  • 地方来自封闭示波器的变量和参数

块还支持两种其他类型的变量:

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

  2. const进口。

最后,方法的实现中,块可以参考物镜-C实例变量 - 请参见对象和块的变量。

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

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

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

  3. 将封闭词法作用域本地的栈(非静态)变量作为const变量捕获。

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

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

    任何更改都反映在封闭词法作用域中,包括在同一个封闭词法作用域内定义的任何其他块。这些在__block存储类型中有更详细的讨论。

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

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

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

+4

还有一个实例变量参考在块的范围内意味着强烈地指向'self',并且实例变量将反映在块执行时出现的任何值。这可能会让人困惑。 – bbum

+1

这个问题问块如何管理捕捉变量。不是捕获变量的语义。 – newacct

19

它实际上相当简单,在锵的模块实现规格说明,在"Imported Variables"部分。

当编译器遇到一个像座:

^{ 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在执行代码时自动进入。因此,代码可以访问结构的成员。

权的声明之后,编译器创建的模块,简单的使用引用变量初始化在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

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

0

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