4

ARC 看起来非常好,但有一两个典型的命名约定/规则对我来说不清楚的极端情况。查看以下围绕 NSThread 的类别实现:

@interface NSThread (BlockAdditions)
- (void)performBlock:(dispatch_block_t)block;
@end

@implementation NSThread (BlockAdditions)
- (void)internal_performBlock:(dispatch_block_t)block
{
    block();
}

- (void)performBlock:(dispatch_block_t)block
{
    [self performSelector:@selector(internal_performBlock:)
                 onThread:self
               withObject:[block copy]
            waitUntilDone:NO];
}

我的问题是:block打电话后会泄漏-copy吗?编译器如何知道何时释放块?Instruments 没有检测到泄漏,但这并不能说服我,鉴于我对 ARC 的了解,这种情况得到了正确处理。感谢您提供任何信息!

4

2 回答 2

5

这会在保留/释放中泄漏,但不应该在 ARC 中泄漏。

编译器看到-copyand 这意味着-release需要 a。如果您查看生成的程序集,那应该正是您所看到的。

(嗯,正是你在完成装配后所看到的,这并不简单。)

请注意,您可以通过仅编译[block copy];.

于 2013-01-20T22:44:50.567 回答
1

编译器看到它block被复制到方法中performBlock:。这会在方法中创建一个新对象,并且在方法中创建的每个对象都必须在方法返回之前释放,除非返回此对象,在这种情况下它必须自动释放,因为一旦方法返回,编译器就不会引用变量在该方法中不再存在,因此永远无法释放它。

所以你的方法大致翻译为

- (void)performBlock:(dispatch_block_t)block
{
    dispatch_block_t blockCopy;

    blockCopy = [block copy];
    [self performSelector:@selector(internal_performBlock:)
                 onThread:self
               withObject:blockCopy
            waitUntilDone:NO];

    // If method returns, how shall the compiler access blockCopy any longer?
    // It can't! And since blockCopy is not returned by the method, it must
    // be destroyed before the method returns.
    [blockCopy release];
}

您可能想知道,为什么这段代码不会崩溃,不会blockCopy被释放?不,因为performSelector:onThread:withObject:waitUntilDone:保留您作为withObject:参数传递给它的对象,直到它执行选择器回调,之后它再次释放该对象。所以当release在 结束时被调用时performBlock:blockCopy有一个retainCount2 并且这个释放将它减少到 1,但不是 0,因此它不会被释放。只有在您的选择器在另一个线程上被调用后,blockCopy才会再次释放,并且由于您调用的选择器没有保留它,它最终会被释放。

于 2013-01-21T01:34:10.587 回答