在Objective-C中按块进行变量捕获的规则

Bre*_*ent 6 objective-c objective-c-blocks

在Objective-C中通过块捕获变量的语义是什么?

#import <Foundation/Foundation.h>

#include <stdio.h>

int main()
{
  NSMutableArray *arr = [NSMutableArray array];
  for (int i = 0; i < 100; ++i) {
    int j = i;
    [arr addObject:^(void) {printf("%d %d\n", i, j); }];
  }
  for (void (^blk)(void) in arr) {
    blk();
  }
}
Run Code Online (Sandbox Code Playgroud)

我正在考虑打印这样的东西:

100 0
100 1
...
100 99
Run Code Online (Sandbox Code Playgroud)

相反,它打印:

99 99
99 99
...
99 99
Run Code Online (Sandbox Code Playgroud)

怎么可能它的解释j是平等的99j在for循环之外甚至没有活着.

rya*_*hee 6

因为你没有使用ARC!没有它,您的块不会被复制.你很幸运,每次都跑到最后一个街区.


mat*_*way 5

99 99多次看到的原因仅仅是由于未定义的行为。

让我们以第一个 for 循环为例:

for (int i = 0; i < 100; ++i) {
  int j = i;
  dispatch_block_t block = ^(void) {printf("%d %d\n", i, j); };
  [arr addObject:block];
}
Run Code Online (Sandbox Code Playgroud)

[为了清楚起见,我已经拉出了块。]

在这个 for 循环中,块被创建。它是在堆栈上创建的,并且永远不会移动到堆中,因为没有块的副本可以这样做。

每次在 for 循环中,很可能(好吧,确实如此)相同的堆栈空间用于该块。然后将块的地址(在堆栈上)添加到arr. 每次都是同一个地址。但每次都是块的新实现。

一旦退出第一个 for 循环,arr包含相同的值 100 次。该值指向最后创建的块,该块仍在堆栈中。但它指向堆栈上的一个块,该块不再安全访问,因为它超出了范围。

然而,在这个例子中,完全靠运气(好吧,简单的代码)被块占用的堆栈空间没有被重用。所以当你去使用这个块时,它“有效”。

正确的解决方案是在将块添加到数组时进行复制。通过致电copy或让 ARC 为您执行此操作。这样,块被复制到堆中,并且您有一个引用计数块,该块将根据数组和创建它的范围的需要而存在。

如果你想更多地了解块的工作原理并更深入地理解这个答案,那么我建议我的解释在这里:

http://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/ http://www.galloway.me.uk/2012/10/a-look-inside- blocks-episode-2/ http://www.galloway.me.uk/2013/05/a-look-inside-blocks-episode-3-block-copy/