clang分析器可以检查返回的基于堆栈的内存。
dispatch_block_t getPrintBlock (const char *msg) {
return ^{
printf("%s", msg);
};
}
引发此错误:returning block that lives on the local stack
这个错误是什么意思?
答案 0 :(得分:8)
错误表示您返回的值在方法返回后将无效。这不仅仅是块的问题,请考虑:
- (int *) badMethod
{
int aLocalIntVariable;
return &aLocalIntVariable; // return a reference to aLocalIntVariable, but that variable is about to die...
}
在输入方法时会创建局部变量,它们所在的位置称为" stack"。当方法返回时,那些局部变量被销毁。您可以在此类变量中返回值,但不能将引用返回给变量本身 - 它将无效。您可以将对局部变量的引用传递给您调用的方法,因为在这种情况下您的局部变量仍然存在。
在您的情况下,您创建了一个块。 Objective-C恰好在堆栈上创建块值,即在匿名局部变量中有效,并使用引用引用它们。你可以将这样的引用传递给你调用的方法,但你不能返回它 - 匿名局部变量就像其他任何一样被销毁。
然而,Objective-C为您提供了两种方法来创建块值的副本作为对象,它存在于"堆",并且将比其创建者寿命更长。首先是Block_copy
这是一个函数:
<reference to heap allocated block> = Block_copy(<reference to stack allocated block>);
这是执行此操作的原始方式,并且每个都支持 - 包括在纯C代码中,块是C的一部分而不仅仅是Objective-C。 假装块的第二种方式已经是一个对象,并允许您发送标准的copy
消息:
<reference to heap allocated block> = [<reference to stack allocated block> copy];
如果你主要是一个Objective-C人,那么第二种方法可能会感觉更舒服,但确实首先模糊了为什么需要它的问题。
ARC有助于自动化内存管理,它会自动将块从堆栈复制到堆中(至少在当前的编译器中,它可能在早期的编译器中无法正常工作),因此程序员可以忽略实际的实现细节附录:ARC
@newacct查询了上面的最后一段,并进行了长时间的Q&amp; A评论交流。为了使信息更容易理解,我们都删除了我们的意见,并在此作为附录整理了这些信息。
在理解ARC如何处理块时,两个文档很有用:
从中可以确定大部分时间 ARC将处理从堆栈到堆的所有块的复制,作为其管理所有对象类型的一部分。
第二个参考文献突出显示了一个案例,至少在撰写文档时,并未自动处理。这种情况是将堆栈分配的块传递给类型为id
的方法参数,例如,类似的东西:
- (void) takeBlockWithTypeLoss:(id)block { ... }
[obj takeBlockWithTypeLoss:^{ ... }];
在这种情况下,在编写文档时,ARC没有复制该块。如果被调用的方法然后执行保留传递的块的操作,则由于保留值不在堆上而发生问题。 (请注意,块需要为要发生的问题分配堆栈。在其环境中不引用变量的文字块是静态分配的,也是首先存储在具有默认强所有权的本地变量中的文字块。然后传递给方法将被复制。)
这种情况是类型丢失的一个例子,已知为块类型的值作为id
丢失类型信息传递。编译器总是可以确定这些点,那么为什么(或者......)ARC没有复制块?过去给出的答案只是效率之一,副本可能不需要,许多不需要的副本都是性能损失。
然而当前编译器(Xcode 4.6.1)出现以处理剩下的这种情况,在类型丢失时,块被复制到堆中。如果任何人都可以证明这已经记录下来了(或者你确信你的编译器处理这种情况,例如通过编码检查)那么它会显示Block_copy()
(或[block copy]
)可以降级到历史记录,如果不是然后当发生类型丢失时,应该使用它。
附录:2013年6月
正如this question所揭示的,有一种情况是Xcode 4.6.3 / Clang 4.2做不处理。当块作为变量参数之一传递给可变参数方法时,编译器不会自动将堆栈块提升到堆。这是上面提到的类型丢失的一个子例子。
因此有一个案例当前编译器无法处理,这表明支持多于规范的编译器没有记录的原因 - 支持是不完整的(尽管这些不是它需要的理论原因)。
与以前一样,如果存在类型丢失,则编译器可能无法自动处理块提升(但如果需要,可以对此进行测试),不涉及类型丢失的情况将根据规范自动处理。
(顺便说一下。对上述问题的评论中提到的older question现在是规范所涵盖的案例之一,并由编译器正确处理。)
答案 1 :(得分:6)
您需要制作块的副本才能将其移动到堆中。
即。类似的东西:
dispatch_block_t createPrintBlock (const char *msg) {
return Block_copy(^{
printf("%s", msg);
}) ;
}