Ros*_*one 3 objective-c objective-c-blocks
clang分析器可以检查返回的基于堆栈的内存.
dispatch_block_t getPrintBlock (const char *msg) {
return ^{
printf("%s", msg);
};
}
Run Code Online (Sandbox Code Playgroud)
引发此错误: 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...
}
Run Code Online (Sandbox Code Playgroud)
输入方法时会创建局部变量,它们所在的位置称为"堆栈".当方法返回时,那些局部变量被销毁.您可以在这样的变量中返回一个值,但不能返回对变量本身的引用 - 它将无效.您可以将对本地变量的引用传递给您调用的方法,因为在这种情况下您的局部变量仍然存在.
在您的情况下,您已创建一个块.Objective-C碰巧在堆栈上创建块值,即在匿名局部变量中有效,并使用引用引用它们.您可以将这样的引用传递给您调用的方法,但不能返回它 - 匿名局部变量就像其他任何一样被销毁.
但是,Objective-C为您提供了两种方法来创建块值的副本作为对象,它存在于"堆"中,并且将比其创建者更长久.首先是Block_copy
功能:
<reference to heap allocated block> = Block_copy(<reference to stack allocated block>);
Run Code Online (Sandbox Code Playgroud)
这是执行此操作的原始方式,并且每个都支持 - 包括在纯C代码中,块是C的一部分而不仅仅是Objective-C.第二种方式假装块已经是一个对象,并允许您发送标准copy
消息:
<reference to heap allocated block> = [<reference to stack allocated block> copy];
Run Code Online (Sandbox Code Playgroud)
如果你主要是一个Objective-C人,那么这第二种方法可能会感觉更舒服,但确实首先模糊了为什么需要它的问题.
ARC有助于自动化内存管理,它会自动将块从堆栈复制到堆中(至少在当前的编译器中,它可能在早期的编译器中无法正常工作),因此程序员可以忽略实际的实现细节.
附录:ARC
@newacct查询了上面的最后一段,并进行了长时间的问答评论.为了使信息更易于理解,我们已经删除了我们的意见,并将此信息作为附录进行了整合.
在理解ARC如何处理块时,两个文档很有用:
从这些可以确定,大多数时候 ARC将处理从堆栈到堆的所有块的复制,作为其管理所有对象类型的一部分.
第二个参考突出显示了一个案例,至少在编写文档时,没有自动处理.这种情况是将堆栈分配块传递给类型的方法参数id
,例如:
- (void) takeBlockWithTypeLoss:(id)block { ... }
[obj takeBlockWithTypeLoss:^{ ... }];
Run Code Online (Sandbox Code Playgroud)
在这种情况下,在编写文档时,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);
}) ;
}
Run Code Online (Sandbox Code Playgroud)