1

我有一个类,它复制一个 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

在我的主要功能中,我对字符串的指针进行了一些调查并遇到了一个问题。

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

StringPointerTest[2253:303] MyString: 0x100100f60 RetainCount:1
StringPointerTest[2253:303] PrintPointer:0x100100f60 RetainCount:2
StringPointerTest[2253:303] PrintPointer:0x100100f60 RetainCount: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
...

4

1 回答 1

0

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

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

于 2013-07-20T01:29:43.613 回答