块文字是否应该保留引用的堆分配块
考虑以下代码:
// t included so block1 is a stack block. See [1] below
int t = 1;
SimpleBlock block1 = ^{ NSLog(@"block1, %d", t); };
// copy block1 to the heap
SimpleBlock block1_copied = [block1 copy];
// block2 is allocated on the stack, and refers to
// block1 on the stack and block1_copied on the heap
SimpleBlock block2 = ^{
NSLog(@"block2");
block1_copied();
block1();
};
[block1_copied release];
// When the next line of code is executed, block2_copied is
// allocated at the same memory address on on the heap as
// block1_copied, indicating that block1_copied has been
// deallocated. Why didn't block2 retain block1_copied?
SimpleBlock block2_copied = [block2 copy];
block2_copied();
[block2_copied release];
为了完整起见,SimpleBlock 的定义如下:
typedef void (^SimpleBlock)(void);
正如代码中的注释所示,我的测试(使用 GCC 4.2 和 LLVM 2.0)显示 block1_copied 在调用 [block2 copy] 时已被释放,然而根据我读过的文档[1,3],块是objective-c对象,块保留它们引用的objective-c对象[2](在非实例变量的情况下)。
另外,请注意,当复制 block2 时,它对 block1 的引用也会更改为对 block1 新副本的引用(与 block1_copied 不同),正如预期的那样,因为块会复制它们引用的任何块 [2]。
那么,这是怎么回事?
A) 如果块保留它们引用的 Objective-C 对象并且块是 Objective-C 对象,为什么在 block2 超出范围之前 block1_copied 被释放?
B) 如果块复制它们引用的块,并且如果将 -(id)copy 发送到堆分配的块实际上只是增加其保留计数,那么为什么在 block2 超出范围之前 block1_copied 被释放?
C) 如果这是预期的行为,解释它的文档在哪里?
[1] http://cocoawithlove.com/2009/10/how-blocks-are -implemented-and.html
[2] http://developer.apple.com/library/ ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html
[3] http://clang.llvm.org/docs/BlockLanguageSpec.txt
脚注:在我的测试中,运行此代码的结果是对 block2_copied() 的无限递归调用,因为 block1_copied() 具有与 block2_copied 相同的内存地址。
Consider the following code:
// t included so block1 is a stack block. See [1] below
int t = 1;
SimpleBlock block1 = ^{ NSLog(@"block1, %d", t); };
// copy block1 to the heap
SimpleBlock block1_copied = [block1 copy];
// block2 is allocated on the stack, and refers to
// block1 on the stack and block1_copied on the heap
SimpleBlock block2 = ^{
NSLog(@"block2");
block1_copied();
block1();
};
[block1_copied release];
// When the next line of code is executed, block2_copied is
// allocated at the same memory address on on the heap as
// block1_copied, indicating that block1_copied has been
// deallocated. Why didn't block2 retain block1_copied?
SimpleBlock block2_copied = [block2 copy];
block2_copied();
[block2_copied release];
Where, for completeness, SimpleBlock is defined by:
typedef void (^SimpleBlock)(void);
As indicated by the comment in the code, my tests (using both GCC 4.2 and LLVM 2.0) show that block1_copied is deallocated by the time [block2 copy] is called, yet according to the documentation that I have read [1,3], blocks are objective-c objects and blocks retain objective-c objects to which they refer [2] (in the non-instance variable case).
Additionally, note that when block2 is copied, its reference to block1 is also changed to a reference to a new copy of block1 (which is different than block1_copied), as expected, since blocks copy any blocks to which they refer [2].
So, what's going on here?
A) If blocks retain objective-c objects to which they refer and blocks are objective-c objects, why is block1_copied deallocated before block2 goes out of scope?
B) If blocks copy blocks to which they refer, and if sending -(id)copy to a heap-allocated block actually just increments its retain count, why is block1_copied deallocated before block2 goes out of scope?
C) If this is the expected behavior, where is the documentation that explains it?
[1] http://cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html
[2] http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html
[3] http://clang.llvm.org/docs/BlockLanguageSpec.txt
Footnote: In my tests, the result of running this code is an infinitely recursive call to block2_copied(), since block1_copied() had the same memory address as block2_copied.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是规范。它现在有点陈旧,并且不具有正常规范的形式主义。然而,C 工作组已经提出了块,并且在该上下文中已经讨论了更正式的规范。
具体来说,规范说:
因此,您所看到的行为是正确的,尽管它绝对是一个陷阱!
在复制块之前,块不会保留任何内容。就像从堆栈开始的块一样,这很大程度上是基于性能的决定。
如果您要将代码更改为:
它的行为符合预期。
静态分析器应该捕获这一点,但没有捕获(请提交错误)。
This is the specification. It is slightly stale right now and doesn't have the formalism of a normal spec. However, Blocks have been proposed in the C working group and a more formal specification has been discussed in that context.
Specifically, the spec says:
Thus, the behavior you are seeing is correct, though it is definitely a pitfall!
A block won't retain anything until the block is copied. Like blocks starting on the stack, this is largely a performance based decision.
If you were to change your code to:
It behaves as expected.
The static analyzer should catch that, but does not (Please file a bug).
我注意到普通物体似乎也会发生同样的情况。此代码:
打印“foo = 43”
这可能是预期的行为。引用苹果的文档:
在释放
block1_copy
时,block2
尚未被复制。I note the same seems to happen with normal objects. This code:
Prints "foo = 43"
It might be expected behavior. To quote Apple's documentation:
At the point
block1_copy
is released,block2
hasn't been copied yet.