MyBlock getBlocks()
{
MyBlock myBlock = ^{
NSLog(@"Hello World!");
};
return myBlock;
}
int main(int argc, const char * argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyBlock myBlock = getBlocks();
myBlock();
[pool drain];
return 0;
}
为什么这段代码有效? myBlock应该被销毁。
顺便说一句,这段代码也有效:
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%ld", [obj retainCount]);
MyBlock myBlock = ^{
NSLog(@"Hello World!");
NSLog(@"%ld", [obj retainCount]);
};
[obj release];
但块中的[obj retainCount]打印1而不是2,为什么?
答案 0 :(得分:4)
出于性能原因,可以在堆栈上分配块,并且只有在复制时才会迁移到堆中。如果您的块没有捕获任何值,编译器也可以将其转换为一个全局块,其内存存在于某个固定的静态内存位置。
由于getBlocks
块没有捕获任何内容,因此它是一个全局块,并且无法处理其内存。它没有引用计数语义,并且retain / release不会执行任何操作。它的引用始终有效,您始终可以调用它。
如果getBlocks
中的块捕获了某些值,那么它将是一个本地堆栈分配的块,并且该方法将返回对该堆栈内存的引用,该内存是当然很危险。在这种情况下,代码甚至可能仍然有效,即使它位于无主堆栈内存中,您也可以调用它(只要该内存在您调用块时没有被其他人删除)。
我相信你的第二个例子也证明了堆栈分配块的副作用。只有在第一次将块实际复制到堆中时,才会保留由堆栈分配的块捕获的对象。这不会发生在您的代码中,因此块不会保留obj
。
这意味着在您的示例中调用[obj release]
之后,该块现在正在捕获一个悬空指针。使用ARC将为您修复所有这些混乱的细节。
有关详细信息,请参阅How blocks are implemented (and the consequences)(Cocoa With Love)。
编辑:此外,必须链接到when to use retainCount
答案 1 :(得分:3)
为什么这段代码有效?
第一个剪切正在工作,因为块实现没有引用周围的范围,因此它被clang配置为 全局 块。这会导致块不像通常那样在堆栈中。
myBlock应该被销毁。
堆栈帧(及其局部变量)不会被销毁。他们的记忆可用于进一步分配。无论如何,由于你的例子简单,你得到一个不在堆栈框架上的全局块。
如果您对周围的作用域进行了任何引用,那么您将拥有一个基于堆栈的块,而myBlock
将是一个悬浮指针,指向一个拥有的内存位置,可能会导致崩溃。
块中的
[obj retainCount]
打印1而不是2,为什么?
听起来很合理。
您正在分配一个对象(保留计数:1),该块保留它(保留计数:2),您正在释放它(保留计数:1),该块最终被执行(保留计数仍为1)。
总的来说,无论如何,在推理记忆时不要依赖retainCount
。内存管理过程有很多内容,你只能通过查看retainCount
的值来明确说出任何内容,因为您无法考虑实现的内部细节。有关此主题的更多信息,请访问:http://whentouseretaincount.com/