如何在ARC和MRC方法之间安全地切换而不需要进行不必要的retain/release调用?

3

我有一个包含以下代码的ARC类:

[object doStuffWithObject:otherObject];

object-doStuffWithObject:方法是使用ARC编译的,并且如下所示:

- (void)doStuffWithObject:(id)otherObject
{
    DoStuffHelper(object, otherObject);
}

DoStuffHelper是一个C函数,出于性能原因,它没有使用ARC进行编译。在DoStuffHelper中,我需要在开始时调用-retain来保留objectotherObject,并在结尾处调用-release来释放它们吗?


4
请解释一下ARC是如何影响性能的。ARC所做的就是在你需要使用MRC手动添加retainrelease的地方自动为你添加适当的调用。 - rmaddy
@meisel ARC 实际上比手动的 retain/release 更快。它避免了自动释放池和在正确的 MRR 代码中无法调用的 retain/release。 - bbum
4个回答

2
在《高级内存管理:实用内存管理》中,参见避免释放正在使用的对象一文,其中指出“接收到的对象通常应在调用方法的范围内保持有效”,即不需要使用retainrelease,但有以下注意事项:

There are occasional exceptions to this rule, primarily falling into one of two categories.

  1. When an object is removed from one of the fundamental collection classes.

    heisenObject = [array objectAtIndex:n];
    [array removeObjectAtIndex:n];
    // heisenObject could now be invalid.
    

    When an object is removed from one of the fundamental collection classes, it is sent a release (rather than autorelease) message. If the collection was the only owner of the removed object, the removed object (heisenObject in the example) is then immediately deallocated.

  2. When a “parent object” is deallocated.

    id parent = <#create a parent object#>;
     // ...
     heisenObject = [parent child] ;
     [parent release]; // Or, for example: self.parent = nil;
     // heisenObject could now be invalid.
    

    In some situations you retrieve an object from another object, and then directly or indirectly release the parent object. If releasing the parent causes it to be deallocated, and the parent was the only owner of the child, then the child (heisenObject in the example) will be deallocated at the same time (assuming that it is sent a release rather than an autorelease message in the parent’s dealloc method).

To protect against these situations, you retain heisenObject upon receiving it and you release it when you have finished with it. For example:

heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];
我会很惊讶如果你的辅助函数属于那些类别之一。你可能不需要使用retainrelease,但为了充分披露,我还是提到了它们。
显然,如果你的函数出于其他原因需要使用retainrelease(例如任务是异步的,或者你正在做某些不寻常的事情,使得对象必须在调用方法的作用域之外存活),那么你可能需要自己实现这些方法,但我猜测如果你正在做这些事情,你会提到它们。
另外,如果你的实用函数创建并返回一个对象,那就是另一回事了(例如,你通常会对返回的对象使用autorelease)。

1
DoStuffHelper是一个C函数,由于性能原因未使用ARC编译。
你有任何性能测量结果表明ARC更慢吗?
一般来说,启用优化器生成的ARC可执行文件将比禁用ARC(并由编译器进行优化)的相同代码更快
这是因为编译器(和运行时)可以推理出引用的点,从而避免调用retainrelease(更糟糕的是autorelease),在正确的MRR代码中,这些调用是强制性的。
如果您有不适用此模式的代码模式,请告诉我并提交错误报告。
(这显然是一个元回答。它质疑是否需要这样一个ARC-MRR接口。维护部分ARC和部分MRR的代码库,这种混合很容易导致脆弱性。)

@meisel。很棒。如果您能分享一个示例,可以通过bugreporter.apple.com提交错误报告。或者,如果您愿意,随时可以将代码发送给我(bbum@),我可以直接提交错误报告。谢谢! - bbum

1
在绝大多数情况下,你不会发现ARC比传统的保留/释放更慢。你用Instruments验证过这个问题吗?
话虽如此,在DoStuffHelper()中你不需要保留/释放对象,因为在进入时retainCount已经是>= 1了。如果将对象存储在静态或全局变量中,则需要保留它们。

如果一个对象是静态或全局的,为什么我需要保留它呢?这似乎不像Rob在他的回答中提到的三种情况之一。 - meisel
当您将对象存储在NSArray或NSDictionary中时,它会自动保留。当您将对象存储在静态或全局变量中时,如果ARC关闭,则不会自动保留。可以这样想:当调用程序时,retainCount为1。您将对象存储在全局变量中,它仍然是1,因为ARC已关闭。您返回给不再需要该对象的调用者并释放它,这将将retainCount减少到零并释放内存。现在,您的全局/静态变量指向已释放的内存,下次访问它时,应用程序可能会崩溃。 - EricS
哦,你特别指的是非ARC文件中的全局变量。这很有道理。 - meisel

1
不,DoStuffHelper不是断言对object或otherObject的所有权的对象。它只是一个直接作用于它们的实用函数。实际上,它是您的ARC类的一部分,该类已经在这些对象上执行完整的内存管理。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接