无法理解块的词法范围

时间:2011-07-11 09:06:48

标签: iphone objective-c objective-c-blocks

为了理解块的词法范围,我编写了以下代码

typedef int (^ MyBlock)(void);

MyBlock b[3];

for (int i=0; i<3; i++) {
    b[i]=^{return i;};
}

for (int i=0; i<3; i++) {
    NSLog(@"%d",b[i]());
}

NSLog(@"----------------------------");

int j=0;

b[0]=^{return j;};  
j++;

b[1]=^{return j;};
j++;

b[2]=^{return j;};

for (int i=0; i<3; i++) {
    NSLog(@"%d",b[i]());
}
  1. 第一次o / p是2,2,2
  2. 第二次o / p为0,1,2
  3. 我预计两个块执行都会有2,2,2。

    有人可以解释一下为什么会这样吗?

2 个答案:

答案 0 :(得分:7)

我假设您一直在阅读bbum’s post on blocks,并且知道您的代码不正确,因为您没有将块从堆栈复制到堆中。

那说:

for (int i=0; i<3; i++) {
    b[i]=^{return i;};
}

在每次迭代中执行以下操作:

  1. 在堆栈中为块变量分配空间。假设它的内存地址是 A ;
  2. 在堆栈中创建块并将其地址( A )分配给b[i];
  3. 在迭代结束时,由于复合语句/范围({})已经结束,弹出堆栈中的任何内容并重置堆栈指针。
  4. 堆栈在每次迭代开始时增长,并在每次迭代结束时收缩。这意味着所有块都在相同的内存地址中创建,即 A 。这也意味着b数组中的所有元素最终都指向同一个块,即最后创建的块。您可以通过运行以下代码来测试它:

    for (int i = 0; i < 3; i++) {
        printf("%p", (void *)b[i]);
    }
    

    应该输出如下内容:

    0x7fff5fbff9e8
    0x7fff5fbff9e8
    0x7fff5fbff9e8
    

    所有元素都指向同一个块,最后一个块在内存地址 A = 0x7fff5fbff9e8中创建。

    另一方面,当您执行以下操作时:

    b[0]=^{return j;};  
    j++;
    
    b[1]=^{return j;};
    j++;
    
    b[2]=^{return j;};
    

    没有复合语句为所有块定义相同的范围。这意味着每次创建块时,其地址都在堆栈的下方,有效地为每个块分配不同的地址。由于所有块都不同,因此它们正确捕获j的当前运行时值。

    如果您按照前面所述打印这些块的地址,您应该得到类似于:

    的输出
    0x7fff5fbff9b8
    0x7fff5fbff990
    0x7fff5fbff968
    

    显示每个块位于不同的内存地址。

答案 1 :(得分:0)

用于迭代b [i]的块数组的i不是每个块中使用的i。您定义的块引用定义期间使用的i。并且我被改为2.然后你用另一个i迭代这些块,但是块仍然引用你在块的定义期间使用的原始i(现在保持值2),尽管我已经“死了“为了该计划的其余部分。 在第二种情况下,您的块也使用一个公共变量,但每次使用它之前都要更改它。

关键是:块始终与定义期间使用的变量相关联。第二个for循环中的i不是块所指的i。

当定义代码已经“死”时,可以调用块。为此,参考变量具有“扩展”实时。它们也可能被运行时移动到堆中。

查看WWDC 2010视频“会议206 - 在iPhone上介绍块和Grand Central Dispatch”。