0
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,为什么?

4

2 回答 2

4

出于性能原因,可以在堆栈上分配块,并且只有在复制它们时才会迁移到堆中。如果您的块没有捕获任何值,编译器还可以将其转换为全局块,其内存存在于某个固定的静态内存位置。

由于您的getBlocks块不捕获任何内容,因此它是一个全局块,并且无法释放其内存。它没有引用计数语义,并且保留/释放不会做任何事情。它的引用将始终有效,您将始终能够调用它。

如果 in 中的块getBlocks 确实捕获了一些值,它将是一个本地堆栈分配的块,并且该方法将返回对该堆栈内存的引用,这当然是非常危险的。在这种情况下,代码甚至可能仍然有效,即使它位于未拥有的堆栈内存中,您也可以调用它(只要在您调用该块时该内存没有被其他人丢弃)。

我相信您的第二个示例也展示了堆栈分配块的副作用。堆栈分配的块捕获的对象仅在该块第一次实际复制到堆时才会保留。这不会发生在您的代码中,因此obj不会被块保留。

这确实意味着[obj release]在您的示例中调用之后,该块现在正在捕获一个悬空指针。使用 ARC 将为您解决所有这些混乱的细节。

有关更多详细信息,请参阅如何实现块(及其后果)(Cocoa With Love)。

编辑:另外,何时使用的强制性链接retainCount

于 2013-08-20T09:46:49.950 回答
3

为什么这段代码有效?

第一个片段正在工作,因为块实现没有对周围范围的引用,因此它被 clang 配置为全局块。这会导致块不像通常那样在堆栈上。

myBlock 应该被销毁。

堆栈帧(及其局部变量)不会被破坏。它们的内存可供进一步分配。无论如何,由于您的示例的简单性,您将获得一个不在堆栈框架上的全局块。

如果您对周围的范围进行任何引用,您将拥有一个基于堆栈的块,并且myBlock将是一个指向拥有内存位置的悬空指针,可能会导致崩溃。

[obj retainCount]在块打印 1 而不是 2,为什么?

听起来很合理。

您正在分配一个对象(保留计数:1),该块正在保留它(保留计数:2),您正在释放它(保留计数:1),该块最终被执行(保留计数仍然为 1)。

retainCount作为一般说明,无论如何,在推理记忆时不要依赖。retainCount内存管理过程在幕后发生了很多事情,由于您不可能考虑到实现的内部细节,因此您不能仅通过查看 的值来肯定地说什么。更多关于这里的主题:http ://whentouseretaincount.com/

于 2013-08-20T10:05:53.980 回答