该错误意味着您返回的值在方法返回后将无效。这不仅仅是块的问题,请考虑:
- (int *) badMethod
{
int aLocalIntVariable;
return &aLocalIntVariable; // return a reference to aLocalIntVariable, but that variable is about to die...
}
局部变量在进入方法时创建,它们所在的地方称为“堆栈”。当方法返回时,这些局部变量将被销毁。您可以返回一个value在这样的变量中,但你不能返回参考变量本身 - 它将是无效的。您可以将对局部变量的引用传递给您调用的方法,因为在这种情况下您的局部变量仍然存在。
在你的情况下,你已经创建了一个块。 Objective-C 碰巧在堆栈上创建块值,即有效地在匿名局部变量中创建块值,并使用引用来引用它们。您可以将这样的引用传递给您调用的方法,但不能返回它 - 该匿名局部变量会像其他任何变量一样被销毁。
然而,Objective-C 提供了两种方法来创建块值的副本作为对象,该副本存在于“堆”上,并且比其创建者的寿命更长。首先有Block_copy
这是一个函数:
<reference to heap allocated block> = Block_copy(<reference to stack allocated block>);
这是执行此操作的原始方法,并且受到所有支持 - 包括在纯 C 代码中,块是 C 的一部分,而不仅仅是 Objective-C。第二种方式pretends该块已经是一个对象,允许您发送标准copy
信息:
<reference to heap allocated block> = [<reference to stack allocated block> copy];
如果您主要是 Objective-C 人员,那么第二种方法可能会感觉更舒服,但确实模糊了为什么需要它的问题。
ARC 在这里很有帮助,在自动化内存管理中,它会自动将块从堆栈复制到堆(至少在当前的编译器中,它在早期的编译器中可能无法正常工作),因此程序员可以忽略真正的实现细节。
附录:ARC
上面的最后一段是由 @newacct 提出的,并产生了很长的问答评论交换。为了使其中的信息更易于理解,我们删除了评论,并将此处的信息合并为附录。
在理解 ARC 如何处理块时,有两个文档很有用:
-
Objective-C 自动引用计数 http://clang.llvm.org/docs/AutomaticReferenceCounting.html,特别是第 3 节(块是可保留对象指针)、3.2.3(可保留对象类型跨返回边界有效)和 7.5(块复制时的规则)。
-
过渡到 ARC 发行说明 http://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html,特别是常见问题解答项目“块在 ARC 中如何工作?”
从这些可以确定大多数时候作为所有对象类型管理的一部分,ARC 将处理从堆栈到堆的所有块复制。
第二个参考文献强调了一种情况,至少在编写文档时,没有自动处理这种情况。这种情况是将堆栈分配的块传递给类型的方法参数id
,例如就像是:
- (void) takeBlockWithTypeLoss:(id)block { ... }
[obj takeBlockWithTypeLoss:^{ ... }];
在这种情况下,在写入文档时,ARC 不会复制该块。如果被调用的方法随后执行保留传递的块的操作,则会出现问题,因为保留值不在堆上。 (请注意,需要为该块分配堆栈才能出现问题。不引用环境中变量的文字块是静态分配的,首先存储在具有默认强所有权的局部变量中,然后传递给方法的文字块也会被复制。)
这种情况是类型丢失的示例,已知为块类型的值被传递为id
丢失类型信息。编译器总是可以确定这些点,那么为什么 ARC 不复制该块呢?过去给出的答案很简单,一个是效率,一个是复制may不需要,并且大量不需要的副本会影响性能。
However当前编译器(Xcode 4.6.1)appears为了处理剩下的一种情况,在类型丢失时将一个块复制到堆中。如果任何人都可以证明这一点现在已记录(或者您确信您的编译器可以处理这种情况,例如通过编码检查),那么它会出现Block_copy()
(or [block copy]
)可以被归入历史,如果没有,那么当发生类型丢失时应该使用它。
附录:2013 年 6 月
据透露这个问题 https://stackoverflow.com/questions/17000926/some-questions-about-objective-c-block-and-copy有一种情况是 Xcode 4.6.3/Clang 4.2 这样做not处理。当一个块作为变量参数之一传递给可变参数方法时,编译器不会自动将堆栈块提升到堆。这是上面提到的类型丢失的一个子情况。
因此,存在当前编译器无法处理的情况,这表明编译器支持超出规范的内容未记录的原因 - 支持不完整(尽管这些并不是理论上的原因)。
因此,和以前一样,如果存在类型丢失,则编译器可能不会自动处理块升级(但如果需要,可以对此进行测试),不涉及类型丢失的情况将根据规范自动处理。
(顺便说一句。老问题 https://stackoverflow.com/questions/6147940/blocks-and-stack对上述问题的评论中提到的现在是规范涵盖的情况之一,并由编译器正确处理。)