4
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    

NSString * str = [[NSString alloc] initWithString:@"test"];
[str release];
int i = 999999999;
while(i-- > 0) {}
NSLog(@"%@", str);
[pool drain];

Output: test

Why didn't release work? How can I immediately delete the object from memory?

Xcode Version 4.0 iPhone Application


~SOLVED~

Thank's to all for answers. I've got a lot of useful information about this question. I'm going to use NSString *str = @"text" instead of NSString *str = [[NSString alloc] initWithString:@"text"];

i've understood that release just "marks" memory as "willing to be freed", but not freeing it immediatly

bartolo-otrit
  • 2,396
  • 3
  • 32
  • 50
  • What exactly is not working here? – onnoweb Apr 12 '11 at 14:10
  • @onnoweb He/She is releasing `str` yet can still log the contents. – Joe Apr 12 '11 at 14:16
  • @Joe - That doesn't imply that the `-release` did not work correctly. Even if `-dealloc` *was* called on that object (and there's no guarantee of that, given a string that was initialized with a literal), that simply means that its memory was marked as available to be reused, not that it necessarily *has* been reused. – Sherm Pendley Apr 12 '11 at 15:26
  • vaychick has stumbled upon an optimization detail of `NSString`. Nothing more. – Dave DeLong Apr 12 '11 at 17:22

5 Answers5

6

Release does work but what you are attempting to do has undefined behavior, and when using a NSString and a literal you may also get different behavior. What is happening is although your object is released the memory at that location is reclaimable and has not changed and when it goes to print it it is still valid. Since it is a NSString a message to description is not necessarily sent and that is why you are not getting an exception for attempting to message a deallocated object.

This question has some good information about NSString and NSLog.

Community
  • 1
  • 1
Joe
  • 56,979
  • 9
  • 128
  • 135
  • 3
    +1 for mentioning that string literals may be treated differently. NSNumber instances created with +numberWithInt: can sometimes behave the same way, returning instances that aren't deallocated when one might naively assume. What's important is correctly balancing one's own memory management - paying no attention to the man behind the curtain! - and the OPs does that correctly. – Sherm Pendley Apr 12 '11 at 15:18
6

It did work. You have relinquished ownership of that object, and when the system determines that it is no longer owned, it will be marked available for reuse by the system. That may happen immediately, if you were the only owner of the string. It may happen at some later point, if creation of the string caused it to be autoreleased internally. Or, as Dave DeLong points out, the system may optimize it into an object that is never released.

In your case, it's being optimized into a constant string, which will exist for the life of the program. If you were to use an NSMutableString instead of an NSString, you'd see funky behavior that would probably not crash, but wouldn't print what you expected. (See this question for an example.)

If you used an NSArray instead, it would be deallocated when you called release, but you'd still see your NSLog example work correctly until you allocated some other object. Deallocation just marks the memory as available for reuse; it doesn't actually clear it out. So if you passed the array to NSLog, that memory hasn't been changed and thus it still prints correctly.

The key point in all of this, though, is to recognize that calling release will not necessarily cause the object to be deallocated. It may continue to exist for any number of reasons. But once you call release, you have relinquished ownership of the object. If you continue using it after that point, the system is free to do all sorts of weird things at its own will, as demonstrated.

Community
  • 1
  • 1
BJ Homer
  • 48,806
  • 11
  • 116
  • 129
  • Doesn't objective c "intentionally" abort or warn you when a message is being sent to a freed object that still hasn't been overriden in the memory – Zaky German Apr 12 '11 at 14:48
  • @Zaky - It can be told to do that, but there's a lot of overhead so it doesn't do it by default. See http://stackoverflow.com/questions/1211923/how-to-use-nszombie-in-xcode – Sherm Pendley Apr 12 '11 at 15:11
  • @Sherm ah yeah i know about NSZombie, i just thought that the same behaviour might happen anyway if the object was freed but the memory was still "intact" – Zaky German Apr 12 '11 at 16:16
4

When you do:

NSString * str = [[NSString alloc] initWithString:@"test"];

This gets optimized into:

NSString * str = @"test";

You can't release a constant string, because it's hardcoded into the application binary.

Proof:

NSString *s = [NSString alloc];
NSLog(@"%p", s);
s = [s initWithString:@"foo"];
NSLog(@"%p", s);
s = @"foo";
NSLog(@"%p", s);

Logs:

2011-04-12 10:17:45.591 EmptyFoundation[6679:a0f] 0x100116370
2011-04-12 10:17:45.599 EmptyFoundation[6679:a0f] 0x100009270
2011-04-12 10:17:45.604 EmptyFoundation[6679:a0f] 0x100009270

You can see that the result of +alloc is different from the result of -initWithString:, and the result of -initWithString: is equivalent to the constant string. Basically, -initWithString: says "aha, i'm going to be an immutable string, and I'm being given an immutable string! I can just take a shortcut, destroy myself, and return the parameter, and everything will still work the same"

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • Since we’re focussing on implementation details… nothing destroys itself. `+[NSString alloc]` returns a singleton instance of `NSPlaceholderString`; all `-[NSString init…]` methods return different objects. – Jens Ayton Apr 12 '11 at 17:42
  • @Ahruman true; the point here though is that the initialized string is the same as the original constant string. – Dave DeLong Apr 12 '11 at 18:22
  • this may be true but it's not really relevant. meaning, you're not going to alter your coding practices b/c of this compiler optimization. the correct answers are from @joe and @BJ Homer. – XJones Apr 12 '11 at 20:36
  • 1
    @XJones I beg to differ. The question is "why does `release` not work?", and the answer is "because you can't release a constant string". – Dave DeLong Apr 12 '11 at 20:50
  • Perhaps we agree to disagree. `release` did work. the code sample is a bug and it's pure chance that `str` still points to memory that hasn't been reclaimed. this is all a developer needs to know to write correct bug free code, regardless of the class or compiler optimizations. – XJones Apr 12 '11 at 20:56
  • 1
    @XJones no, `release` did *not* work, because it did not do what the OP was expecting. He was expecting it to trigger a call to `dealloc`. However, since the string is constant, the string cannot be deallocated. I do agree that this is an implementation "detail", but in the context of the question, it *is* the correct answer. – Dave DeLong Apr 12 '11 at 21:03
  • @XJones however, I concede that invoking `release` is the correct thing to do, strictly from a convention point-of-view. Since he alloc'd an object, he is responsible for relinquishing ownership of it via `release` or `autorelease`. In that sense, `release` did work. However, the OP was getting confused because he was expecting the object to be destroyed, and it wasn't. – Dave DeLong Apr 12 '11 at 21:09
  • 2
    I've vowed not to get sucked in to debates on esoterica, but here I am. Let's say you're 100% right. You are doing @vaychick a disservice if you are suggesting that he/she needs to worry about this. The question was not just about why `release` doesn't work. It was also about how can they be sure that the object is "immediately deleted from memory". I want my devs to write great code, not worry about the underlying specifics of how an OS reclaims memory or compiler optimizations. There are domains where that may be appropriate but not iOS development. – XJones Apr 12 '11 at 21:10
  • 1
    @XJones i don't disagree with you. The point of my answer was to show that what the OP was suggesting is not possible, and this is why. The optimizations of `NSString` have thrown many developers for a loop about why the normal `retain`-`release` methods don't work as expected (including me!). `NSString` has been so heavily optimized that it's almost always a bad example for stuff like this. – Dave DeLong Apr 12 '11 at 21:15
1

You're using a bad pointer in you NSLog(). You happen to be getting lucky in this case, but you should expect code like this to crash or fail in other ways.

Caleb
  • 124,013
  • 19
  • 183
  • 272
0

There is no need to delete the memory block, this will use up an unneeded cycle. The memory will be overridden when an new object is allocated an occupy that memory block.

rckoenes
  • 69,092
  • 8
  • 134
  • 166