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

Adr*_*ian 3 memory objective-c

我正在学习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;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用带雪豹的mac.要测试它使用了多少内存,我会在它运行的同时打开Activity Monitor.几秒钟后,它正在使用千兆字节的内存.我该怎么办才能让它不那么用?

ken*_*ytm 5

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

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

  1. 不要-release:

    str = [NSString stringWithCString: "Name" encoding: NSUTF8StringEncoding];
    person.name = str;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 用途-init:

    str = [[NSString alloc] initWithCString: "Name" encoding: NSUTF8StringEncoding];
    person.name = str;
    [str release];
    
    Run Code Online (Sandbox Code Playgroud)

同样,在-[Person init]:

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

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

不同之处在于

str = [NSString stringWithCString: "Name" encoding: NSUTF8StringEncoding];
Run Code Online (Sandbox Code Playgroud)

相当于

str = [[[NSString alloc] initWithCString:......] autorelease];
Run Code Online (Sandbox Code Playgroud)

一个-autoreleased对象是指"的所有权转让给最近的NSAutoreleasePool,并让后来释放它".

多迟?默认情况下,当前运行循环勾选一次.但是,这里没有明确的运行循环*,因此运行循环没有运行.自动释放池永远不会有机会清除这10 分配的临时字符串.

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


注意:

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