为什么内存有时会立即释放,有时仅在自动释放池耗尽时?

时间:2014-04-08 22:39:45

标签: objective-c

我做了简单的实验,发现了一些奇怪的行为。这里有一些代码 - 启用ARC的长方法的一部分:

MKObject *obj = [[MKObject alloc]init];
NSMutableArray *temp = [[NSMutableArray alloc]init];
[temp addObject:obj];
obj = nil;
temp = nil;
//here deallocating is called on obj (MKObject)
//other stuff

但如果我将NSMutableArray更改为NSArray并将文字初始化

NSArray *temp = @[obj];

在自动释放池关闭之前执行解除分配,而不是在将nil设置为所有引用之后执行。我错过了什么吗?

3 个答案:

答案 0 :(得分:2)

一些观察结果:

  1. 在您的第一个示例中,MKObjectNSMutableArray都不是自动释放对象,因此这些对象将立即释放,而不是等待自动释放池耗尽:

    MKObject *obj = [[MKObject alloc] init];
    NSMutableArray *temp = [[NSMutableArray alloc] init];
    [temp addObject:obj];
    obj = nil;
    temp = nil;
    
  2. 在第二个示例中,NSArray是一个自动释放对象,因此在自动释放池耗尽之前,NSArray(因此MKObject)将不会被释放

    MKObject *obj = [[MKObject alloc] init];
    NSArray *temp = @[obj];
    obj = nil;
    temp = nil;
    

    要理解数组文字@[]创建自动释放对象的原因,应该注意它扩展为+[NSArray arrayWithObjects:count:]。每当使用alloc后跟init(无论是init还是其中一个排列,例如initWithObjects:)之外的任何方法实例化对象时,都会创建自动释放对象

  3. 正如您所观察到的,当应用创建自动释放对象时,该对象不会立即被释放,但是当自动释放池耗尽时。由于我们通常会快速回流到runloop(此时池将被耗尽),因此在简单情况下,自动释放对象或非自动释放对象的选择几乎没有实际影响。但是,例如,如果应用程序有一个for循环,它会在其中创建许多自动释放对象而不会返回到runloop,则可能会出现问题(特别是如果MKObject很大或者您正在执行此操作次)。例如:

    for (NSInteger i = 0; i < 100; i++) {
        MKObject *obj = [[MKObject alloc] init];
        NSArray *temp = @[obj];
    
        // Note, because they are local variables which are falling out of scope, I don't have to manually `nil` them.
    }
    

    因为我们在此示例中实例化自动释放NSArray对象,所以上面会将所有100个数组和对象保留在内存中,直到您返回到runloop并且自动释放池有机会耗尽。这意味着应用程序的高水位标记&#34; (它在任何给定时间使用的最大内存量)将高于它可能需要的值。您可以通过以下方式解决此问题:

    • 使用非自动释放对象(例如使用alloc / init)而不是使用数组文字:

      for (NSInteger i = 0; i < 100; i++) {
          MKObject *obj = [[MKObject alloc] init];
          NSArray *temp = [[NSArray alloc] initWithObjects:obj, nil];
      }
      

    • 引入您自己的,明确声明的@autoreleasepool

      for (NSInteger i = 0; i < 100; i++) {
          @autoreleasepool {
              MKObject *obj = [[MKObject alloc] init];
              NSArray *temp = @[obj];
          }
      }
      

    在这个最后的例子中,自动释放池将在for循环的每次迭代中耗尽,解决自动释放对象的任何挑战,否则这些挑战将延迟释放,直到循环结束。

  4. 最后一点需要注意:一般来说,以allocinit(或init方法的变体)开头的方法,不会生成自动释放对象,而所有其他方法(如arrayWithObjects:count: )将生成自动释放对象。一个值得注意的例外是NSString类,由于内部内存优化,它不符合此规则。因此,如果您有任何疑问,如果您反复实例化和释放对象,则可以使用自己的手册@autoreleasepool,并且您不确定对象是否是自动释放对象。而且,与往常一样,使用仪器中的分配工具分析您的应用程序是观察应用程序高水位的好方法。有关这些不同技术如何影响应用内存使用情况的说明,请参阅https://stackoverflow.com/a/19842107/1271826

答案 1 :(得分:1)

该数组由自动释放池保留。如Clang的Objective-C Literals文档中所述,数组文字语法扩展为对+[NSArray arrayWithObjects:count:]的调用,这将创建一个自动释放的数组。

答案 2 :(得分:1)

我看到的一些事情,虽然我对这个问题并不完全清楚所以我不能说哪个适用:

将对象添加到NSArray或NSMutableArray会增加对象的保留计数。 在第一个实例中,您手动实例化obj,这使它保留计数1。 将其添加到NSMutableArray会使其保留计数为2。 在这种情况下,obj = nil减少保留为1; temp = nil告诉数组处理释放其内容。那些w / retain count 0会立即被dealloc'd。

在创建@ []文字的第二个实例中,引擎盖下的文字语法使用方法arrayWithObjects:count:创建一个自动释放的对象。当不再需要它时,它会进入自动释放池以进行最终释放。

这不是数组中对象的问题,而是数组本身的创建方式。

编辑我最初的回复以解决以下评论 - 我对这个问题感到困惑。