Objective-C: Weak attritube don't work as expected [duplicate]
Asked Answered
C

2

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.

Cysteine answered 2/1, 2013 at 4:56 Comment(4)
@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.Ebonieebonite
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...Annorah
Also here: #9203310Annorah
That question in #9203310 is somehow resolved to me earlier. I understand well the idea of initWithFormat and stringWithFormat. Anyway, thanks for your concern, I appriciate it. :)Cysteine
S
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.

Spannew answered 2/1, 2013 at 7:12 Comment(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.Cysteine
E
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
Ebonieebonite answered 2/1, 2013 at 7:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.