9

我知道__block如果一个访问它的块被复制,一个变量将从堆栈移动到堆中。但是下面的测试代码告诉我变量在块复制之前__block被移动到堆中。

也就是说,四个输出是:stack => heap => heap => heap,这不是我预期的结果:stack => stack => stack => heap。

有人可以纠正我吗?

__block int x = 0;
int *pointerToX = &x;
//1. It's on the stack
NSLog(@"x's location is on the stack: %p", &x);
int (^block)() = ^{
    x += 1;
    return x;
};

//2. I think its stack, but it's heap
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //it's heap not stack

block();
//3. I think its stack, but it's heap
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //it's heap not stack

block = [block copy]; // The variable x will be moved to the heap
//4. I think its stack, but it's heap
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //heap
4

3 回答 3

3

让我先说:块很奇怪。

现在,当您开始时,您已经声明了一个变量 x,并在它前面加上了__block. 到底是什么__block鬼?好吧,对于在块的词法范围内捕获的对象,变量被-retain'ed 以保证它们在块执行时就在附近。但是对于原始变量,块通过强制它们按const值而不是按引用传递来保护它们的值。通过添加__block,您可以在复制块时让编译器自由支配您的变量从堆栈“神奇地”移动到堆中。需要明确__block的是,变量实际上是堆栈分配的,但是在malloc()复制块时它们被移动到堆('d)。

但是 x 位置的奇怪变化呢?好吧,再回到__block过去。因为你没有const像普通变量那样使用对 x 的引用,所以块使用了一个(有点烦人的)技巧:一个块创建一个指向任何__block变量的指针,如果该变量发生了变异,它就会被取消引用。达达!您的变量没有从堆栈移动到堆,该块只是取消引用指向它的指针并将其移动到内存中!

因此,实际上,您对变量在何处以及何时移动感到困惑。您的示例正在记录正确的值。

于 2013-02-26T07:06:02.550 回答
2

您的预期输出基于您的假设,即直到步骤 3-4 才复制块。但是,Blocks 规范中的任何内容都不能保证会是这种情况。

是的,最迟当您明确调用该块时,该块将被复制-copy。但是为什么不能早点复制呢?早点复制一个块永远不会错。因此,何时确切地复制一个块是未定义的,您不应该依赖它。

ARC 下的一些最新版本的编译器可能会比较保守,并在创建块后立即复制它。没有什么不妥。同样,如果这样做,那将是一个实现细节,其他编译器或未来版本可能会做一些不同的事情。

于 2013-02-26T08:49:21.640 回答
0

我在 In Objective-C with ARC 上问了同样的问题,当我定义一个块时编译器会做什么?

如果您在 ARC 中运行代码

在 ARC 中,

可保留对象所有者类型的块变量通过使用从堆栈副本中移动的结果初始化堆副本而从堆栈中移出。

http://clang.llvm.org/docs/AutomaticReferenceCounting.html#blocks

于 2013-02-26T14:04:58.550 回答