clang 分析器可以检查返回的基于堆栈的内存。
dispatch_block_t getPrintBlock (const char *msg) {
return ^{
printf("%s", msg);
};
}
引发此错误:returning block that lives on the local stack
这个错误是什么意思?
clang 分析器可以检查返回的基于堆栈的内存。
dispatch_block_t getPrintBlock (const char *msg) {
return ^{
printf("%s", msg);
};
}
引发此错误:returning block that lives on the local stack
这个错误是什么意思?
该错误意味着您返回的值在方法返回后将无效。这不仅仅是块的问题,请考虑:
- (int *) badMethod
{
int aLocalIntVariable;
return &aLocalIntVariable; // return a reference to aLocalIntVariable, but that variable is about to die...
}
局部变量是在输入方法时创建的,它们所在的地方称为“堆栈”。当一个方法返回时,这些局部变量被销毁。你可以在这样的变量中返回一个值,但你不能返回对变量本身的引用——它将是无效的。您可以将对局部变量的引用传递给您调用的方法,因为在这种情况下您的局部变量仍然存在。
在您的情况下,您已经创建了一个块。Objective-C 碰巧在堆栈上创建块值,即有效地在匿名局部变量中,并使用引用来引用它们。您可以将这样的引用传递给您调用的方法,但不能返回它——匿名局部变量就像其他任何变量一样被销毁。
然而,Objective-C 为您提供了两种方法来创建块值的副本作为对象,它存在于“堆”上,并且比它的创建者更长寿。首先是Block_copy
which 是一个函数:
<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 查询的,结果是长长的问答评论交流。为了使信息更容易理解,我们都删除了我们的评论,我将这里的信息合并为附录。
在了解 ARC 如何处理块时,有两个文档很有用:
从这些可以确定,大部分时间ARC 将处理从堆栈到堆的所有块复制,作为其所有对象类型管理的一部分。
第二个参考文献强调了一种情况,即至少在编写文档时没有自动处理。这种情况是堆栈分配的块被传递给类型的方法参数id
,例如:
- (void) takeBlockWithTypeLoss:(id)block { ... }
[obj takeBlockWithTypeLoss:^{ ... }];
在这种情况下,在编写文档时,ARC 没有复制该块。如果被调用的方法然后执行保留传递的块的操作,则会出现问题,因为保留值不在堆上。(请注意,该块需要堆栈分配才能发生问题。在其环境中不引用变量的文字块是静态分配的,也是一个文字块,它首先存储在具有默认强所有权的局部变量中,然后传递给该方法将被复制。)
这种情况是类型丢失的示例,已知是块类型的值作为id
丢失类型信息传递。编译器总是可以确定这些点,那么为什么(或做过..)ARC 不复制该块?过去给出的答案只是效率之一,可能不需要副本,并且大量不需要的副本会影响性能。
然而,当前的编译器(Xcode 4.6.1)似乎可以处理剩下的这种情况,在类型丢失时,一个块被复制到堆中。如果有人可以证明这已记录在案(或者您确信您的编译器会处理这种情况,例如通过编写检查代码),那么它会出现Block_copy()
(或[block copy]
)可以归入历史记录,如果没有,那么当发生类型丢失时应该使用它。
附录:2013 年 6 月
正如这个问题所揭示的,有一种情况是 Xcode 4.6.3/Clang 4.2无法处理的。当一个块作为变量参数之一传递给可变参数方法时,编译器不会自动将堆栈块提升到堆。这是上面提到的类型损失的一个子情况。
因此,存在当前编译器无法处理的情况,这表明编译器支持超过规范的原因是未记录的 - 支持不完整(尽管这些不是理论上的理由)。
所以和以前一样,如果存在类型丢失,那么编译器可能不会自动处理块提升(但如果需要,可以对其进行测试),不涉及类型丢失的情况会根据规范自动处理。
(顺便说一句。在对上述问题的评论中提到的旧问题现在是规范涵盖的情况之一,并由编译器正确处理。)
您需要复制块以将其移动到堆中。
即类似的东西:
dispatch_block_t createPrintBlock (const char *msg) {
return Block_copy(^{
printf("%s", msg);
}) ;
}