【问题标题】:Correct use of autorelease正确使用自动释放
【发布时间】:2011-06-26 00:04:08
【问题描述】:

我正在尝试了解为什么我的应用程序崩溃并且我正在检查我的代码。我很确定这是对自动释放的有效使用:

(部分代码)

- (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 返回一个自动释放对象不是吗?我在理解自动释放对象的范围方面也有一些困难。自动释放的对象(如果在这种情况下)是否会在调用该代码所属的方法的整个方法中被池保留?

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

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

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

【问题讨论】:

    标签: iphone objective-c autorelease


    【解决方案1】:

    这是有效的,但是——真的——你可以完全跳过[NSArray arrayWithArray:ids];,因为不需要创建新数组。

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

    The documentation explains it all better than I.


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

    如果是返回的实例变量,是的,创建不可变副本对于避免后续突变意外“转义”至关重要。

    您是否阅读过内存管理文档?具体来说,关于autorelease pools的部分?它很清楚自动释放是如何工作的。我讨厌解释一个权威的作品。

    【讨论】:

    • 返回不可变数组不是更好吗?
    • Bbum:你介意解释一下这是什么意思吗,我还是不明白 runloop,所以如果你能说它在返回它的方法范围内是否有效,它至少会对我有所帮助.
    【解决方案2】:

    [NSArray arrayWithArray:] 返回一个自动释放的对象。如果您希望codeThatInvokesTheCodeAbove 拥有该阵列的所有权,您应该在其上调用retain(并根据苹果的指南重命名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

    所以,要回答您的问题,是的,您发布的代码是有效的。是否正确取决于您要完成的任务。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        如果你想在返回之前释放非可变列表,你可以使用这样的东西:

         - (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
        }
        

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

        【讨论】:

          猜你喜欢
          • 2011-02-25
          • 2011-05-07
          • 2011-11-09
          • 2011-01-30
          • 2016-06-24
          • 2014-12-17
          • 2011-05-03
          • 1970-01-01
          相关资源
          最近更新 更多