无法理解块的词法范围

Abh*_*eet 6 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]());
}
Run Code Online (Sandbox Code Playgroud)
  1. 第一次o/p是2,2,2
  2. 第二次o/p是0,1,2

我期待两个块执行的2,2,2.

任何人都可以解释一下为什么会这样?

小智 7

我假设你一直在阅读bbum关于块的帖子,并且知道你的代码不正确,因为你没有将块从堆栈复制到堆中.

那说:

for (int i=0; i<3; i++) {
    b[i]=^{return i;};
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 在堆栈中为块变量分配空间.假设它的内存地址是A ;
  2. 在堆栈中创建块并将其地址(A)分配给b[i];
  3. 在迭代结束时,由于复合语句/ scope({})已经结束,弹出堆栈中的任何内容并重置堆栈指针.

堆栈在每次迭代开始时增长,并在每次迭代结束时收缩.这意味着,在相同的存储器地址被创建的所有块,即.这也意味着b数组中的所有元素最终都指向同一个块,即最后创建的块.您可以通过运行以下代码来测试它:

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

应输出如下内容:

0x7fff5fbff9e8
0x7fff5fbff9e8
0x7fff5fbff9e8
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

b[2]=^{return j;};
Run Code Online (Sandbox Code Playgroud)

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

如果你打印前面描述的那些块的地址,你应该得到类似于的输出:

0x7fff5fbff9b8
0x7fff5fbff990
0x7fff5fbff968
Run Code Online (Sandbox Code Playgroud)

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