2013-03-14 48 views
3

铛分析器可以检查返回的基于堆栈的内存。位于本地堆栈上的返回块

dispatch_block_t getPrintBlock (const char *msg) { 
    return ^{ 
     printf("%s", msg); 
    }; 
} 

引发此错误:returning block that lives on the local stack

这个错误是什么意思?

+0

我猜测这意味着,无论你是返回不一个函数返回时一直存在的值... – Floris 2013-03-14 20:04:14

+0

请参阅http://stackoverflow.com/questions/6147940/blocks-and-stack – sfstewman 2013-03-14 20:05:23

回答

5

您需要复制块以将其移动到堆中。

即是这样的:

dispatch_block_t createPrintBlock (const char *msg) { 
    return Block_copy(^{ 
     printf("%s", msg); 
    }) ; 
} 
+0

为什么'-copy'优先于'Block_copy'? – 2013-03-14 20:15:47

+1

@RosePerrone为什么不呢?这取决于上下文,但你说得对,在C函数中'Block_copy'会更好。 – Sulthan 2013-03-14 20:23:31

+0

我同意...修正。谢谢。 – nielsbot 2013-03-14 21:15:31

8

的错误意味着你是返回,这将是该方法返回后无效的值。这不只是个街区的问题,请考虑:创建

- (int *) badMethod 
{ 
    int aLocalIntVariable; 

    return &aLocalIntVariable; // return a reference to aLocalIntVariable, but that variable is about to die... 
} 

局部变量进入的方法时,他们居住的地方被称为“堆栈”。当一个方法返回时,这些局部变量被销毁。您可以在这样一个变量中返回一个,但不能将参考返回给变量本身 - 它将无效。您可以将对局部变量的引用传递给您调用的方法,因为在这种情况下您的局部变量仍然存在。

在你的情况下,你已经创建了一个块。 Objective-C恰好在堆栈上创建块值,即在匿名局部变量中生效,并使用引用来引用它们。您可以将这样的引用传递给您调用的方法,但不能返回它 - 匿名局部变量与其他任何方法一样被销毁。

然而,Objective-C为您提供了两种方法来创建块值的副本作为一个对象,它位于“堆”上,并且将超过其创建者。首先出现的是Block_copy这是一个功能:

<reference to heap allocated block> = Block_copy(<reference to stack allocated block>); 

这是做到这一点的原始的方式,并支持每一个 - 包括纯C代码,块是C的一部分,而不是仅仅的Objective-C。第二种方式假装块是一个对象已经并允许您发送标准copy消息:如果你主要是一个Objective-C的人第二种方法可能感觉更舒服

<reference to heap allocated block> = [<reference to stack allocated block> copy]; 

,但模糊问题为什么需要摆在首位。

ARC在这里帮助自动化内存管理,它会自动将块从堆栈复制到堆(至少在当前的编译器中,它可能无法在早期的编译器中正常工作),因此程序员可以忽略实际的实现详情。

附录:ARC

上面的最后一段被@newacct和长Q &注释交换导致查询。为了使这些信息更容易遵循,我们都删除了我们的评论,并将这些信息作为附录加以整理。

在了解了ARC如何处理块两个文件是有用的:

  1. Objective-C Automatic Reference Counting,特别是第3(块可保留对象的指针),3.2.3(可保留对象类型是跨越边界的回报有效)和7.5(块复制时的规则)。
  2. Transitioning to ARC Release Notes,特别是常见问题解答项目“块如何在ARC中工作?”

从这些可以判定大部分时间 ARC将处理块的所有复制从栈堆作为其所有对象类型的管理的一部分。

第二个参考文献突出显示了一个案例,即至少在编写文档时没有自动处理。在这种情况下,堆栈分配块被传递给类型为id的方法参数,例如,例如:

- (void) takeBlockWithTypeLoss:(id)block { ... } 

[obj takeBlockWithTypeLoss:^{ ... }]; 

在这种情况下,在编写文档时,ARC没有复制该块。如果被调用的方法执行保留传递块的操作,则会发生问题,因为保留值不在堆上。 ()请注意,需要对块进行堆栈分配以便发生问题。在其环境中不引用变量的文本块是静态分配的,也是一个首先存储在具有默认强权的局部变量中的文本块,然后通过该方法将被复制

这种情况是一个类型丢失的例子,一个已知为块类型的值被作为id丢失类型信息。编译器总是可以确定这样的点,那么为什么(或者......)ARC不能复制块?过去给出的答案仅仅是效率之一,不需要副本,并且大量不需要的副本是性能问题。

然而当前编译器(4.6.1的Xcode)出现来处理这种一个剩余的情况下,在块被复制到堆的类型损失的点。如果任何人都可以证明这是现在记录的(或者你确信你的编译器处理这种情况,例如通过编码检查),那么它会出现Block_copy()(或[block copy])可以降级到历史,如果不是那么当发生类型损失时应该是用过的。

附录:2013年6月

所揭示的this question有Xcode中那4.6.3/4.2锵不办案。当一个块作为可变参数之一传递给可变参数方法时,编译器不会自动将堆栈块提升到堆中。这是上面提到的类型丢失的子情形。

因此,目前的编译器无法处理,这表明支持超过规范的编译器没有文档 - 支持不完整(尽管这不是理论上的理由)。因此,如果存在类型丢失,那么编译器可能不会自动处理块升级(但是如果需要,可以对其进行测试),不涉及类型丢失的情况根据规范自动处理。

(顺便说一句,在上述问题的评论中提到的older question现在是由规范覆盖和编译器能够正确处理的案例之一。)