【问题标题】:Objective-C: autoreleased object as parameter for methodObjective-C:自动释放的对象作为方法的参数
【发布时间】:2011-09-22 16:15:38
【问题描述】:

下面的代码会不会导致问题?

- (void) method1 {
      NSMutableArray *myArray = [[[NSMutableArray alloc] init] autorelease];

      ... fill the array

      [someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}

我的应用有时会出现崩溃,这似乎是个问题;但我不明白...... myArray 不应该至少在方法1结束之前保持活力吗?

非常感谢您的帮助!

所以除了“这可能是个问题”之外,我的问题是: - 删除自动释放并在方法结束时进行释放就足够了吗? - 如果不是:我是否必须在“someMethod”中进行保留/释放?

编辑:但这可能是个问题,对吗?

- (void) method1 {
      NSMutableArray *myArray = [self getArray];

      ... fill the array

      [someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}

- (NSMutableArray) method2 {
      return [[[NSMutableArray alloc] init] autorelease];
}

【问题讨论】:

  • Cocoa 的所有权规则指定接收到的对象应该在调用方法的整个范围内保持有效,除非您执行以下两种操作之一:(1) 从集合中删除对象或 (2) 释放“父对象”。见Avoid Causing Deallocation of Objects You’re Using

标签: objective-c memory-management


【解决方案1】:

我暂时假设您的意思是NSMutableArray,而不是NSArray。如果你在这里真的是说NSArray,那么这一切都是不可能的。您不能在不可变数组上“填充数组”。

是的,这应该可以。您的问题可能在其他地方。这里的一个常见错误是过度释放您放入 myArray 的内容。

【讨论】:

    【解决方案2】:

    看起来您在分配数组的行上缺少几个左括号。应该是[[[NSMutableArray alloc] init] autorelease];

    【讨论】:

    • 我通常喜欢使用更简单(更短)的[NSMutableArray array];,而不是包装 3 个调用。 FWIW…
    【解决方案3】:

    您的代码完全正确。不要听人告诉你在调用someMethod:之后删除自动释放并手动释放数组。

    在 99% 的情况下,使用自动释放对象绝对不会对您的应用程序的性能产生负面影响。您唯一需要担心的是循环。

    [[[Foo alloc] init] autorelease] 模式与使用内置帮助方法(如[NSString stringWithFormat:...])相同。它们都返回一个自动释放的对象,但您可能不必担心后者的性能。同样,只需担心大循环中的自动释放,或者当 Instruments 告诉您有问题时。

    使用[[[Foo alloc] init] autorelease] 样式还可以将所有内存管理放在一行中。如果您习惯键入[[[,您将不会忘记发布。如果您正在复制和粘贴代码,或者四处移动代码,您不会不小心丢失相应的release,因为它们都在同一行。

    审查使用[[[Foo alloc] init] autorelease] 样式的代码也更容易,因为您不必在方法的后面四处寻找release 以确保内存管理正确。

    因此,就可读性、安全性和正确性而言,您的代码绝对好。这适用于您的原始 sn-p 和您在下面添加的后续操作。只有当你有大循环时,自动释放的性能才会成为一个问题。

    您的崩溃问题一定是由您发布的代码中不明显的其他因素引起的。

    如果您还没有阅读Memory Management Programming Guideline,我还建议您阅读。基本上,自动释放的对象保证在封闭池被释放之前保持有效。在您的情况下,自动释放池存在于调用堆栈(可能是主运行循环)中较高的位置,因此您可以放心,您的自动释放数组将在您进行的任何调用期间保持有效。

    最后一点。您的数组分配代码也可以使用 array 辅助构造函数:

    NSMutableArray *myArray = [NSMutableArray array];
    

    这比原来的更简单、更简洁、更短。

    【讨论】:

    • 感谢您的回答,迈克。但是你确定第二个代码是 100% 健壮的吗?我从method2获得的数组是否有可能在“填充数组”部分的某个地方释放?不太可能,但仍然可能 - 不是吗?
    • 只要您的代码遵循所有权规则,就不会出错。在您的代码返回堆栈之前,从 method2/getArray 返回的数组不会被自动释放。如果“填充数组”代码在没有执行保留/复制/等操作的情况下对数组执行释放,那么这只是代码中的错误,是的,可能会出错。但是,如果您的所有代码都是正确的,并且您只释放您拥有所有权的对象(即您拥有分配/保留/复制的对象),那么一切都很好。
    【解决方案4】:

    最好的做法是:

    - (void) method1 {
          NSMutableArray *myArray = [[NSMutableArray alloc] init];
    
          ... fill the array
    
          [someObject someMethod:myArray]; // someObject does work on that array (without retain!)
          [myArray release];
    }
    

    最好从两个不同的角度。一种方法是你持有你的阵列直到你不需要它,所以在你说之前它不会从内存中擦除。第二个是同一句话,唯一的原因是你会尽快从不再需要的对象中清理内存。
    最后一个可能需要更多解释......自动释放的对象应该尽快自动释放您不需要它,但它们是根据两个基本规则发布的:在块结束时(不总是)或在块结束后的内存压力下(总是)。换句话说,NSAutoreleasePool 并不总是在某些代码块的末尾释放东西,而且将这些发布延迟到以后的时间甚至都很少见。
    无论如何,您应该始终检查是否过度释放您的对象,因为当您尝试在代码中访问此类对象时会导致崩溃。

    【讨论】:

    • 主自动释放池实际上会在每个运行循环结束时耗尽:developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…
    • 确实如此,但并非每个方法都是运行循环,因此您会发现自己有大量内存用于长时间不需要的东西(就程序时间而言,不是秒左右)当你需要它来做其他事情时,直到它在当前运行循环结束时被释放
    猜你喜欢
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 2011-11-09
    相关资源
    最近更新 更多