代码之家  ›  专栏  ›  技术社区  ›  Adrian

为什么这个程序占用这么多内存?

  •  3
  • Adrian  · 技术社区  · 14 年前

    我正在学习目标C。我正在尝试释放我使用的所有记忆。所以,我写了一个程序来测试我是否做得对:

    #import <Foundation/Foundation.h>
    
    #define DEFAULT_NAME @"Unknown"
    
    @interface Person : NSObject
    {
      NSString *name;
    }
    @property (copy) NSString * name;
    @end
    
    @implementation Person
    @synthesize name;
    - (void) dealloc {
      [name release];
      [super dealloc];
    }
    - (id) init {
      if (self = [super init]) {
        name = DEFAULT_NAME;
      }
      return self;
    }
    @end
    
    
    int main (int argc, const char * argv[]) {
      NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
      Person *person = [[Person alloc] init];
      NSString *str;
      int i;
    
      for (i = 0; i < 1e9; i++) {
        str = [NSString stringWithCString: "Name" encoding: NSUTF8StringEncoding];
        person.name = str;
        [str release];
      }
    
      [person release];
      [pool drain];
      return 0;
    }
    

    我用的是一个Mac和雪豹。为了测试它使用了多少内存,我在活动监视器运行的同时打开它。几秒钟后,它将使用千兆字节的内存。我该怎么做才能让它不用那么多呢?

    1 回复  |  直到 14 年前
        1
  •  5
  •   kennytm    14 年前

    首先,您的循环不正确。 +stringWithCString:… 不是一个 +alloc / +new… / -copy 方法,所以您不应该 -release 它。

    其中任何一个都是正确的:

    1. 不要 释放 :

      str = [NSString stringWithCString: "Name" encoding: NSUTF8StringEncoding];
      person.name = str;
      
    2. 使用 -init :

      str = [[NSString alloc] initWithCString: "Name" encoding: NSUTF8StringEncoding];
      person.name = str;
      [str release];
      

    同样地,在 -[Person init] :

    - (id) init {
      if ((self = [super init])) {
        name = [DEFAULT_NAME copy]; // <----
      }
      return self;
    }
    

    现在,如果您使用variant 1,那么内存应该会像您以前看到的那样增长到千兆字节,而variant 2应该是一个非常恒定的小值。

    区别在于

    str = [NSString stringWithCString: "Name" encoding: NSUTF8StringEncoding];
    

    等于

    str = [[[NSString alloc] initWithCString:......] autorelease];
    

    -autorelease d object的意思是“将所有权转移到最近的nsautoreleasepool,稍后再释放”。

    多晚?默认情况下,当前运行循环会勾选一次。但是,这里没有显式的运行循环*,因此运行循环没有运行。自动释放池从来没有机会清除这10个 分配的临时字符串。

    但是,对于变量2,临时字符串会立即释放,因此临时字符串不会填满内存。(我们不需要等待池内的水冲出“池”,不涉及池内的水。)


    注:

    *:A run loop 是附加到每个运行线程的唯一循环。如果编写一个cli实用程序,就很少需要有run循环。