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
解释为等于 99
? j
甚至在 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/
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
解释为等于 99
? j
甚至在 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/