10

Possible Duplicate:
Why do weak NSString properties not get released in iOS?

I'm a newbie to Objective C and I've got some questions that I cannot answer myself. I have a block of code for testing __weak variable (I'm using ARC, of course):

NSString *myString = [[NSString alloc] initWithFormat:@"John"];
NSString * __weak weakString = myString;
myString = nil; //<-- release the NSString object
NSLog(@"string: %@", weakString);

The output of the above codes is as expected, since weakString is a weak variable :

2013-01-02 11:42:27.481 ConsoleApp[836:303] string: (null)

But when I modified the code to this:

NSString *myString = [[NSString alloc] initWithFormat:@"John"];
NSString * __weak weakString = myString;
NSLog(@"Before: %@", weakString); //<--- output to see if the __weak variable really works.
myString = nil;
NSLog(@"After: %@", weakString);

The output is totally not what I expected:

2013-01-02 11:46:03.790 ConsoleApp[863:303] Before: John
2013-01-02 11:46:03.792 ConsoleApp[863:303] After: John

The output of the latter NSLog must have been (nil) instead of "John". I've tried to search in many documents but I haven't found the answer for this case. Can someone give an reasonable explaination? Thanks in advance.

Community
  • 1
  • 1
hoang Cap
  • 704
  • 6
  • 18
  • @jrturton: I don’t think this is a duplicate of the linked question. The issue there was using a constant `NSString` which does not participate in the usual memory management because of performance optimizations. The poster here uses `initWithFormat` to avoid precisely this issue. – zoul Jan 02 '13 at 07:16
  • I've read (but couldn't find this time) another dupe of this where some optimisation of NSString prevents this working. If the OP tries a different type of object, I suspect everything will work as expected. I'll keep searching... – jrturton Jan 02 '13 at 07:21
  • Also here: http://stackoverflow.com/questions/9202810/lifetime-of-weak-local-variables-with-arc – jrturton Jan 02 '13 at 07:25
  • 1
    That question in http://stackoverflow.com/questions/9202810/lifetime-of-weak-local-variables-with-arc is somehow resolved to me earlier. I understand well the idea of **initWithFormat** and **stringWithFormat**. Anyway, thanks for your concern, I appriciate it. :) – hoang Cap Jan 02 '13 at 09:59

2 Answers2

7

The NSLog function is retaining the passed NSString in an autorelease pool. The zeroing-weak variable will therefore not be zeroed until the autorelease pool has drained. For example:

__weak NSString* weakString = nil;

@autoreleasepool {
    NSString* myString = [[NSString alloc] initWithFormat:@"Foo"]; // Retain count 1
    weakString = myString;         // Retain count 1
    NSLog(@"A: %@", weakString);   // Retain count 2
    NSLog(@"B: %@", weakString);   // Retain count 3
    myString = nil;                // Retain count 2
    NSLog(@"C: %@", weakString);   // Retain count 3

    NSAssert(weakString != nil, @"weakString is kept alive by the autorelease pool");
} 

// retain count 0
NSAssert(weakString == nil, @"Autorelease pool has drained.");

Why is NSLog putting the string into an autorelease pool? That's an implementation detail.

You can use the debugger or Instruments to follow the retain count of the NSString instance. The exact retain counts are unimportant, but it does shed some light as to what's going on behind the scenes. What is important is that the NSString instance is deallocated when the autorelease pool is drained.

Darren
  • 25,520
  • 5
  • 61
  • 71
  • 1
    Yeah, this pretty much explained it, I was thinking that the NSlog() will increase the retain count of its parameter(s), but your answer is much more reasonable. Thanks so much Darren. – hoang Cap Jan 02 '13 at 10:36
0

I think it’s just some implementation detail. Your weak variable is getting cleared, but not just immediately. For example, this works as expected:

NSString *myString = [[NSString alloc] initWithFormat:@"John"];
NSString * __weak weakString = myString;
@autoreleasepool {
    NSLog(@"Before: %@", weakString);
    myString = nil;
}
NSLog(@"After: %@", weakString); // nil
zoul
  • 102,279
  • 44
  • 260
  • 354