【问题标题】:Why does this program take up so much memory?为什么这个程序占用这么多内存?
【发布时间】:2010-06-05 18:10:59
【问题描述】:

我正在学习 Objective-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。为了测试它使用了多少内存,我在它运行的同时打开了 Activity Monitor。几秒钟后,它正在使用千兆字节的内存。我该怎么做才能让它不使用那么多?

【问题讨论】:

    标签: objective-c memory


    【解决方案1】:

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

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

    1. 不要-release:

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

    现在,如果您使用变体 #1,内存应该像您之前看到的那样上升到千兆字节,而变体 #2 应该是一个相当恒定的小值。

    区别在于

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

    等价于

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

    -autoreleased 对象的意思是“将所有权转移到最近的 NSAutoreleasePool,并让它稍后释放”。

    多晚?默认情况下,它是当前运行循环滴答一次的时间。但是,这里没有明确的运行循环*,所以运行循环没有运行。自动释放池永远没有机会清除这 109 个分配的临时字符串。

    但是,对于变体 #2,临时字符串会立即释放,因此临时字符串不会填满内存。 (我们不需要等待池刷新——不涉及池。)


    注意:

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

    【讨论】: