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
是否泄漏?编译器如何知道何时释放块?仪器没有检测到泄漏,但鉴于我对ARC的了解,这并不能说服我这个案例得到正确处理。感谢您的任何信息!
答案 0 :(得分:5)
这会在保留/释放中泄漏,但不应在ARC中泄漏。
编译器看到-copy
,这意味着需要-release
。如果你看一下生成的程序集,那应该就是你所看到的。
(嗯,正如你在整个装配过程中所看到的那样,这不是很简单。)
请注意,您只需编译[block copy];
即可简化程序集。
答案 1 :(得分: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
的{{1}}为2,此版本将其降低为1,但不会降为0,因此不会释放。只有在另一个线程上调用了选择器之后,才会再次释放retainCount
,并且由于被调用的选择器没有保留它,它最终将被解除分配。