4

我在 ARC 下运行没有自动释放池的代码:

- (NSString*) outName {
    if (!outName) {
        outName = [[NSString alloc] initWithFormat:@"whatever"]; // or stringWithFormat
    }
    return outName;
}

调试器说它 每次都在泄漏单个实例而没有适当的池。 outName

如果我将代码更改为

- (NSString*) outName {
    if (!outName) {
        outName = @"whatever";
    }
    return outName;
}

这是我做不到的(这个例子显然是简化的)。此外,如果我在调用代码中创建一个自动释放池(我想避免),泄漏消息就会消失。

为什么 ARC 坚持自动释放这个保存在strong属性中的对象?更重要的是,我怎样才能避免这个警告?

4

4 回答 4

2

这是一个所有权问题。

让我们谈谈您首先分配给自己的 NSString。当你分配一个对象时,堆中的内存是为那个对象保留的(除非你 allocWithZone: 到另一个位置)。保留计数隐含地为 1,并且您拥有该对象,即您有责任在完成后释放它。如果您要返回一个指向该对象的指针,即返回该对象,您并没有完全放弃确保该对象不泄漏的责任。您不能释放它,因为保留计数将变为 0,并且该对象将被释放。你自动释放它,确保在你的运行循环结束时(或更早),对象将被释放并可能被释放。如果返回的对象需要存活更长时间,调用函数负责保留返回的对象。

如果没有自动释放池,您将泄漏,因为指定的 autoReleasePool 为空(请记住它可以发送消息为空,这就是为什么这不仅会崩溃而不仅仅是泄漏)。

带有固定 @"whatever" 的示例不会泄漏,因为编译器会为该字符串保留程序内存,而 -release 对它们没有影响。对于一些低价值的 NSNumbers 也是如此。

正如 James 所说,ARC 并没有删除保留发布和自动发布的概念。

编辑: outName 如何声明为 ivar/property?

于 2012-06-25T15:03:46.387 回答
0

When ARC returns the ivar (or any object) to the calling method, it has to guarantee that it won't be released before the current RunLoop is finished. As a programmer you know that it will never be released, but ARC relies on algorithmic guarantees (best practices). ARC will call retain] autorelease] unless the variable points to a constant (not instantiated with NARC), and is therefore not at risk for being released.

You should not avoid this warning. You need to fix your code. You can add an autorelease pool. If you do not wish to do this, the other way is to push the logic that uses the ivar down to the object that is actually holding the ivar.

于 2012-07-09T05:10:56.173 回答
0

Jared 的回答很好,但 ARC 的部分工作方式是使用命名约定。的方法名称outName意味着它返回一个autoreleased值,因此如果有一个 de-ARC-ifier,您的最后一行将如下所示:

return [[outName retain] autorelease];

显然这需要一个自动释放池。

这在您的第二个示例中不会发生,因为您要返回一个常量,因此保留/自动释放会被优化掉。

于 2012-06-25T15:18:40.037 回答
-1

奇怪,我有一个非常相似的方法,我使用它并且它不会泄漏。它看起来像这样:

-(NSString *) dataFilePath {

    NSString *appendPath;
    if (isPowerMode==YES) {
        appendPath = kDataFileNamePower;
    } else {
        appendPath = kDataFileNameClassic;
    }

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    return [documentsDirectory stringByAppendingPathComponent:appendPath];
}       

所以也许从那里开始,你想在方法的开头声明一个空的 NSString 指针并填充并返回它而不是 outName。

于 2012-06-25T14:51:03.680 回答