Question

I have a class that copy's an NSString and print's it's retain count and address.

@interface TestStringPointer : NSObject

@property (copy) NSString *stringTest;

- (void)printPointer;

@end

@implementation TestStringPointer

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

@end

In my main function I was doing a bit of investigation on the String's pointer and ran into an issue.

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

When I debug the app it crashes the 3rd time through the while loop (the number of times through the loop varies). I understand that the copy doesn't occur cause the string isn't mutable. After it's released in main, I would've expected it to go back to 1.
1) If an object isn't autoreleased, is it still affected the autorelease pool?
2) Wouldn't having a max retain count prevent the object from being drained in the pool, or does that flag it for deletion?
3) Shouldn't the copy have stepped in at some point and actually made a copy before it was deleted?

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

If I modify main and remove the pool

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

All is right... Forever...

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

Was it helpful?

Solution

The mistake is when you release myString. This is wrong because stringWithUTF8String returns an autoreleased string (remember that every method has the autoreleased version: a static method, and the non-autoreleased version: init or initWithSomething: ). So when the autorelease pool gets drained, myString retain count goes to zero and the object gets deallocated (maybe later, you don't know exactly when, the fact that it crashes at the 3rd loop iteration is casual).

So you solve the problem by calling alloc + initWithUTF8String: instead of stringWithUTF8String:. The fact that you see an incredibly high retain count is just dued to the fact that the memory has been freed and maybe written again, before the object gets really deallocated, it's just a pitfail, you don't own the object anymore.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top