带有自动释放池的@property 复制和手动内存管理

Posted

技术标签:

【中文标题】带有自动释放池的@property 复制和手动内存管理【英文标题】:@property copy & manual memory management with an autorelease pool 【发布时间】:2013-07-20 01:01:42 【问题描述】:

我有一个类可以复制一个 NSString 并打印它的保留计数和地址。

@interface TestStringPointer : NSObject

@property (copy) NSString *stringTest;

- (void)printPointer;

@end

@implementation TestStringPointer

- (void)printPointer 
  NSLog(@"PrintPointer:%p\n RetainCount:%lu", _stringTest, [_stringTest retainCount]);


@end

在我的 main 函数中,我对字符串的指针进行了一些调查,但遇到了一个问题。

int main(int argc, const char * argv[])

  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  TestStringPointer *test = [[TestStringPointer alloc] init];

  NSString *myString = [NSString stringWithUTF8String:"Hello World"];
  NSLog(@"MyString: %p \n RetainCount:%lu", myString, [myString retainCount]);

  [test setStringTest:myString];
  [test printPointer];
  [myString release];
  [pool drain];

  while (1) 
    [test printPointer];
  

  return 0;

当我调试应用程序时,它通过 while 循环第三次崩溃(通过循环的次数不同)。我知道复制不会发生,因为字符串不可变。 在它主要发布后,我预计它会回到 1。 1) 如果一个对象没有自动释放,它仍然会影响自动释放池吗? 2) 最大保留计数不会阻止对象在池中被耗尽,还是将其标记为删除? 3) 副本不应该在某个时候介入并在被删除之前实际制作了副本吗?

StringPointerTest[2253:303] MyString: 0x100100f60 RetainCount:1 StringPointerTest[2253:303] PrintPointer:0x100100f60 RetainCount:2 StringPointerTest[2253:303] PrintPointer:0x100100f60 保留数:1152921504606846975 StringPointerTest[2253:303] PrintPointer:0x100100f60 RetainCount:1152921504606846975

如果我修改 main 并删除池

int main(int argc, const char * argv[])


  TestStringPointer *test = [[TestStringPointer alloc] init];

  NSString *myString = [NSString stringWithUTF8String:"Hello World"];
  NSLog(@"MyString: %p \n RetainCount:%lu", myString, [myString retainCount]);

  [test setStringTest:myString];
  [test printPointer];
  [myString release];

  while (1) 
    [test printPointer];
  

  return 0;

一切都好……永远……

StringPointerTest[423:303] MyString: 0x10010a670 RetainCount:1 StringPointerTest[423:303] PrintPointer:0x10010a670 RetainCount:2 StringPointerTest[423:303] PrintPointer:0x10010a670 RetainCount:1 ...

【问题讨论】:

【参考方案1】:

错误是在您释放myString 时。这是错误的,因为stringWithUTF8String 返回一个自动发布的字符串(请记住,每个方法都有自动发布的版本:静态方法和非自动发布的版本:init 或 initWithSomething: )。因此,当自动释放池耗尽时,myString 保留计数变为零并且对象被释放(也许稍后,您不知道确切的时间,它在第三次循环迭代时崩溃的事实是偶然的)。

所以你通过调用alloc + initWithUTF8String: 而不是stringWithUTF8String: 来解决问题。您看到一个令人难以置信的高保留计数的事实只是由于内存已被释放并可能再次写入,在对象真正被释放之前,这只是一个陷阱,您不再拥有该对象。

【讨论】:

保留计数高的原因是该对象可以被他调用的任何 API 函数保留和自动释放任意次数。保留计数通常永远不会有用。

以上是关于带有自动释放池的@property 复制和手动内存管理的主要内容,如果未能解决你的问题,请参考以下文章

在帧写入循环中使用和不使用自动释放池的内存占用,为啥?

如何手动释放Linux内存?

关于自动释放池的讲解与介绍

自动释放池的前世今生 ---- 深入解析 autoreleasepool

如何手动释放Linux内存的方法

29-oc自动释放池