Question

I am a little bit stubborn, but I want to understand weak and strong references well, so that's why I'm asking you once again.

Consider this:

__weak NSString* mySecondPointer = myText;   
NSLog(@"myText: %@", myText);

The result is myText: (null) and it is pretty obvious - weak reference is set to null just after assignment, cause there is no strong reference to the pointed object.

But in this case:

__strong NSString* strongPtr = [[NSString alloc] initWithFormat:@"mYTeSTteXt %d"]; 
// weak pointer points to the same object as strongPtr
__weak NSString* weakPtr = strongPtr;
if(strongPtr == weakPtr) 
     NSLog(@"They are pointing to the same obj");        
NSLog(@"StrongPtr: %@", strongPtr);
NSLog(@"weakPtr: %@", weakPtr);

NSLog(@"Setting myText to different obj or nil");

// after line below, there is no strong referecene to the created object:
strongPtr = [[NSString alloc] initWithString:@"abc"];  // or myText=nil;

if(strongPtr == weakPtr) 
     NSLog(@"Are the same");
else
     NSLog(@"Are NOT the same");
NSLog(@"StrongPtr: %@", strongPtr);
// Why weak pointer does not point to nul
NSLog(@"weakPtr: %@", weakPtr);

The output:

2013-03-07 09:20:24.141 XMLTest[20048:207] They are pointing to the same obj
2013-03-07 09:20:24.142 XMLTest[20048:207] StrongPtr: mYTeSTteXt 3
2013-03-07 09:20:24.142 XMLTest[20048:207] weakPtr: mYTeSTteXt 3
2013-03-07 09:20:24.143 XMLTest[20048:207] Setting myText to different obj or nil
2013-03-07 09:20:24.143 XMLTest[20048:207] Are NOT the same
2013-03-07 09:20:24.144 XMLTest[20048:207] StrongPtr: abc
2013-03-07 09:20:24.144 XMLTest[20048:207] weakPtr: mYTeSTteXt 3   // <== ??

My question:

Why after strongPtr = [[NSString alloc] initWithString:@"abc"]; weak pointer value is not changed to nil (why the object created at the beginning still exists in memory, despite it does not have any strong refs? -- or maybe it has?)


I have tried that one: (but it is not good for adding a comment I suppose). I have included the code where I am creating a strongPtr in @autorealesepool. I not sure if it is correct solution but it work...

 __strong NSString* strongPtr;
    __weak NSString* weakPtr;
    @autoreleasepool {


        strongPtr = [[NSString alloc] initWithFormat:@"mYTeSTteXt %d", 3];

        // weak pointer point to object create above (there is still strong ref to this obj)
        weakPtr = strongPtr;
        if(strongPtr == weakPtr) NSLog(@"They are pointing to the same obj");        

        NSLog(@"StrongPtr: %@", strongPtr);
        NSLog(@"weakPtr: %@", weakPtr);

        NSLog(@"Setting myText to different obj or nil");   

    // after line below, there is no strong referecene to the created object:
     strongPtr = [[NSString alloc] initWithString:@"abc"];  


    }

    if(strongPtr == weakPtr) 
        NSLog(@"Are the same");
    else
        NSLog(@"Are NOT the same");
    NSLog(@"StrongPtr: %@", strongPtr);
    // Why weak pointer does not point to nul
    NSLog(@"weakPtr: %@", weakPtr);

Output:

2013-03-07 09:58:14.601 XMLTest[20237:207] They are pointing to the same obj
2013-03-07 09:58:14.605 XMLTest[20237:207] StrongPtr: mYTeSTteXt 3
2013-03-07 09:58:14.605 XMLTest[20237:207] weakPtr: mYTeSTteXt 3
2013-03-07 09:58:14.606 XMLTest[20237:207] Setting myText to different obj or nil
2013-03-07 09:58:14.607 XMLTest[20237:207] Are NOT the same
2013-03-07 09:58:14.607 XMLTest[20237:207] StrongPtr: abc
2013-03-07 09:58:14.608 XMLTest[20237:207] weakPtr: (null)
Was it helpful?

Solution

From the assembly code it can be seen that accessing weakPtr generates a objc_loadWeak call.

According to the Clang documentation, objc_loadWeak retains and autoreleases the object and is equivalent to

id objc_loadWeak(id *object) {
  return objc_autorelease(objc_loadWeakRetained(object));
}

This (hopefully) explains why both

if(strongPtr == weakPtr) ...

and

NSLog(@"weakPtr: %@", weakPtr);

create additional autoreleased references.

This is not a special NSString problem, I could reproduce the same behaviour with a custom (plain) class.

OTHER TIPS

First, don’t experiment with weak references or other memory management behaviour on NSString, there’s too much magic in that class. Not that weak references wouldn’t work with NSString, just the behaviour is slightly trickier than you would expect and easily leads to incorrect conclusions. See these previous questions:

When you wrap your code example with an autorelease pool and log the weak string pointer afterwards, it’s nil indeed. It might even be the case that you would get similar behaviour with classes other than NSString – you are simply not guaranteed that the weak references will be cleared at the precise moment you lose the last strong reference to an object. Or maybe you are, but it’s hard to tell when exactly the last strong reference disappears because of the autorelease pools in play, as hinted to by this example (and nicely explained by Martin’s answer).

when you do

strongPtr = [[NSString alloc] initWithString:@"abc"]

you strongPtr is pointing to new allocated object, and since the previous object it was pointing too didn't get deallocated, the weak pointer still points to a valid address.

btw. you can print the memory address off an object with

NSLog(@"%@", [NSString stringWithFormat:@"%p", theObject])

Not sure the OP's question and/or the accepted answer here is still valid, at least not as of the results I'm seeing with iOS9/Xcode7.

Here's a (slightly cleaned up) version of the OP's code...

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool
    {
        NSString* __strong strongPtr = [[NSString alloc] initWithFormat:@"Life, Universe, Everything: %d", 42];
        NSString* __weak   weakPtr   = strongPtr;

        NSLog(strongPtr == weakPtr ? @"Same" : @"Different");
        NSLog(@"  StrongPtr: %@", strongPtr);
        NSLog(@"  weakPtr:   %@", weakPtr);

        NSLog(@"Changing strongPtr to something else...");
        // After this is set, there is no strong reference to the created object
        strongPtr = [[NSString alloc] initWithFormat:@"Drink: %@", @"Pan-galactic Gargle Blaster!"];

        NSLog(strongPtr == weakPtr ? @"Same" : @"Different");
        NSLog(@"  StrongPtr: %@", strongPtr);
        NSLog(@"  weakPtr:   %@", weakPtr);
    }

    return 0;
}

And here's the (truncated) output...

Same
  StrongPtr: Life, Universe, Everything: 42
  weakPtr:   Life, Universe, Everything: 42

Changing strongPtr to something else...

Different
  StrongPtr: Drink: Pan-galactic Gargle Blaster!
  weakPtr:   (null)

Program ended with exit code: 0

Here accessing the weak references in the conditionals (as per the accepted answer's explanation) doesn't keep an auto-released reference around as you can see by the (null) in the output.

...or did I accidentally change the OP's question to the point where I hid what he's seeing? Or perhaps it's because now ARC is on by default?

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