【问题标题】:Why is a __block variable is moved to the heap BEFORE the block is copied? [closed]为什么在复制块之前将 __block 变量移动到堆中? [关闭]
【发布时间】:2013-02-11 11:59:19
【问题描述】:

我知道如果访问它的 Block 被复制,__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

【问题讨论】:

  • 你为什么要关心这个?如果变量从堆栈移动到堆,__block int x; int *ptr = &x; 会发生什么?
  • 这是一个实现细节。你应该尽量不要想太多。
  • 记录块的类(是的,块是对象)也有助于确定它被复制到哪里(__NSStackBlock____NSMallocBlock__)。
  • 这是一个有趣的问题,我认为它不应该被关闭。

标签: ios objective-c objective-c-blocks heap-memory


【解决方案1】:

让我先说:块很奇怪。

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

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

所以,实际上,您对变量的移动位置和时间感到困惑。您的示例正在记录正确的值。

【讨论】:

  • “但是对于原始变量,块通过强制它们通过 const 值而不是通过引用来保护它们的值。”这适用于所有类型。非__block变量,无论类型如何,在创建块时按值捕获。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    我在In Objective-C with ARC, what does compiler do when I define a block?问了同样的问题

    如果您在 ARC 中运行代码

    在 ARC 中,

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

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

    【讨论】:

    • 引用其他帖子时,请确保包含正确的引用。
    • 是的,它是由 ARC 引起的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-14
    • 2018-01-13
    • 2015-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多