正确使用自动释放

时间:2011-06-26 00:04:08

标签: iphone objective-c autorelease

我试图理解为什么我的应用程序崩溃并且我正在浏览我的代码。我很确定这是自动释放的有效用途:

(部分代码)

- (NSArray *)allQuestionsFromCategories:(NSArray *)categories {

    ...

    NSMutableArray *ids = [[[NSMutableArray alloc] init] autorelease];

    while (sqlite3_step(statement) == SQLITE_ROW) {
        [ids addObject:[NSNumber numberWithInt:sqlite3_column_int(statement, 0)]];
    }

    return [NSArray arrayWithArray:ids];
}

这有效吗? NSArray arrayWithArray返回一个autorelease对象吗?我在理解自动释放对象的范围方面也遇到了一些困难。自动释放的对象(如果是在这种情况下)是否会通过调用此代码所属方法的方法保留池?

- (void)codeThatInvokesTheCodeAbove {
    NSArray *array = [self.dao allQuestionsFromCategories];
    ...
}

返回的数组在整个codeThatInvokesTheCodeAbove方法中是否有效而不保留它?如果是的话,它会更长有效吗?

了解其范围时遇到了一些问题,以及何时应该保留自动释放对象。

4 个答案:

答案 0 :(得分:4)

这是有效的,但是 - 实际上 - 您可以完全跳过[NSArray arrayWithArray:ids];,因为不需要创建新数组。

自动释放的对象在自动释放池耗尽之前一直有效,这通常在每次通过运行循环时发生一次(或“通过GCD将事件排入队列时定期执行,但从不在块执行时”)。

The documentation explains it all better than I.


无需创建不可变数组,因为返回将有效地“向上转换”NSMutableArrayNSArray。虽然这在运行时没有意义,但这意味着调用者无法编译对返回值的变异方法的调用,而不使用强制转换来避免警告。在这种情况下避免警告的铸造是邪恶的缩影,没有能干的开发者会这样做。

如果它是一个返回的实例变量,那么,是的,创建一个不可变副本对于避免后续突变“意外逃逸”至关重要。

您是否阅读过内存管理文档?具体来说,关于autorelease pools的部分?它很清楚自动释放是如何工作的。我不想解释一项明确的工作。

答案 1 :(得分:1)

[NSArray arrayWithArray:]返回一个自动释放的对象。如果您希望codeThatInvokesTheCodeAbove获得数组的所有权,则应在其上调用retain(并根据Apple的指南重命名codeThatInvokesTheCodeAbove)。否则,如果您不关心对象的所有权是不明确的,那么您的代码就可以了。

换句话说,[NSArray arrayWithArray:]返回一个您不拥有的数组,但至少在此运行周期内您可以访问它。因此,codeThatInvokesTheCodeAbove至少可以在此运行周期内访问它。所有权不明确,因为没有人打电话给alloccopynewmutableCopyretain。隐含的是NSArray在返回新数组之前调用autorelease,从而放弃了所有权。

我的信息来自http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html%23//apple_ref/doc/uid/20000994-BAJHFBGH

所以,回答你的问题,是的,你发布的代码是有效的。它是否正确取决于你想要完成的是什么。

答案 2 :(得分:1)

自动释放的对象是标记为稍后释放的对象。 有一个非常特殊的对象由UIApplicationMain自动创建:UIRunLoop。 想象它就像一个while结构,它循环直到应用程序终止,它接收每个事件并正确地将它重新发送给你感兴趣的类。就在UIApplicationMain之前,有一个命令来创建一个NSAutoreleasePool,一旦NSRunLoop准备就绪,就会附加到它。当您向对象发送-autorelease命令时,NSAutoreleasePool会记得在释放时释放它。在具有较少内存的平台(iOS设备)中多次使用它是危险的,因为当您发送-autorelease命令但是当NSAutoreleasePool被耗尽时(当应用程序释放它时),对象不会被释放。

答案 3 :(得分:0)

如果您想在返回之前释放非可变列表,可以使用以下内容:

 - (NSArray *)allQuestionsFromCategories:(NSArray *)categories {

    ...

    NSArray* result;
    NSMutableArray *ids = [[NSMutableArray alloc] init]; // AUTORELEASE REMOVED FROM HERE


    while (sqlite3_step(statement) == SQLITE_ROW) {
        [ids addObject:[NSNumber numberWithInt:sqlite3_column_int(statement, 0)]];
    }
    result = [NSArray arrayWithArray:ids]; // COPY LIST BEFORE IT IS FREED.
    [ids release]; // MUTABLE LIST FREES _NOW_

    return result; // NONMUTABLE COPY IS RETURNED
}

除非你的可变数组有时可能会占用大量内存,否则不值得这样做。