objective-C 中按块捕获变量的规则

Rules for variable capture by block in objective-C

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();
  }
}

我希望它能打印出如下内容:

100 0
100 1
...
100 99

而是打印:

99 99
99 99
...
99 99

它怎么可能将 j 解释为等于 99j 甚至在 for 循环之外都不存在。

因为你没有使用 ARC!没有它,您的块不会被复制。你只是走运,运行 每次都是最后一个区块。

您多次看到 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];
}

[为了清楚起见,我把方块去掉了。]

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

每次围绕 for 循环,极有可能(嗯,确实如此)相同的堆栈 space 用于该块。然后将块的地址(在堆栈上)添加到 arr。每次都是同一个地址。但是每次都是块的新实现。

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

然而,在这个例子中,堆栈 space 被块占用的堆栈(好吧,简单的代码)没有被重用。所以当你去使用这个块时,它 "works".

正确的解决方法是在将块添加到数组时复制块。通过调用 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/