Why does my object still work after countless releases? - objective-c

I can never seem to deallocate my NSMutableString as shown below. The initial retain count should be 1, but after releasing several times, the string is still usable like nothing happened!
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSMutableString* s = [[NSString alloc]initWithString:#"AAA"];
[s release];
[s release];
[s release];
[s release];
[s release];
NSLog(#"%#",s);
[pool drain];
return 0;
}
Of course, if I use Analyze it still tells me that I release a released object on the second release.

Scott's answer is the correct general one, but in this particular case, the reason is that NSString literals (i.e. #"") are uniqued compile-time constants and do not actually do anything at all when retained and released. Your assignment of it to an NSMutableString* does not actually make it an NSMutableString, so what you've written is equivalent to
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];
[#"AAA" release];

Releasing an object tells the runtime that it can destroy the object, at least as far as you're concerned, but it doesn't require that the object be destroyed immediately: After your first [s release], Cocoa is free to do whatever it pleases with the memory formerly used by s. It might give that memory to the next object that does an alloc, in which case your later attempts to access s will result in a fiery runtime crash… or it might not need that memory right away, in which case you might get away with accessing a released object.
The rule of thumb is less "I've released this object, which means it no longer exists" and more "I've released this object, which means it's no longer guaranteed to exist."

Related

Allocation and release problem

I'm trying to explain memory management on iOS to a friend and I'm showing him a wrong code. But when I'm launching the app, it's working and I don't know why.
Here's the snippet :
NSString *myString = [[NSString alloc] initWithString:#"myString"];
[myString release];
NSLog(#"%#",myString);
I don't understand why my NSLog is working ...
Do you have some explanations ?
Thanks !
There are two things to bear in mind with regard to your example.
As MByD explained, accessing an object that’s been deallocated is undefined behaviour. It might or might not crash your program — it depends on whether the memory that had been allocated to that object has been reused and what’s been put there. In some cases the memory hasn’t been reused yet (and you might think the object is still alive but that object is in fact a ghost object), in other cases the memory may have been reused by another Objective-C object (the program won’t crash but you’ll see a different object) and in other cases the memory may have been reused by something that’s not an Objective-C object (in which case the program will likely — but not necessarily — crash).
Your string object is a constant string. As explained in the answer to this question and its comments, a constant string is never deallocated. When you send -[NSString initWithString:] passing a constant string as the argument, Cocoa returns the original constant string so your code is effectively the same as NSString *myString = #"myString";. This is an internal Cocoa implementation detail though. Production code should always consider that objects returned by +alloc (and, usually, subsequent -init) are owned by the caller, should be released when the caller is not interested in them anymore, and won’t be available after deallocation.
For the sake of experimentation, try the following code:
#import <Foundation/Foundation.h>
#include <stdio.h>
int main(void) {
[NSAutoreleasePool new];
NSString *s1 = [[NSString alloc] initWithString:#"myString"];
NSString *s2 = [[NSString alloc] initWithString:#"myString"];
NSString *s3 = [[NSString alloc] initWithString:#"myString"];
NSString *s4 = [[NSString alloc] initWithString:#"myString"];
printf("s1 = %p\n", s1);
printf("s2 = %p\n", s2);
printf("s3 = %p\n", s3);
printf("s4 = %p\n", s4);
[s1 release];
[s2 release];
[s3 release];
[s4 release];
return 0;
}
Conceptually, s1, s2, s3, s4 should be different objects. However, by running this program you can see that they are effectively the same object (they have the same address):
$ ./a.out
s1 = 0x10080b090
s2 = 0x10080b090
s3 = 0x10080b090
s4 = 0x10080b090
This is an undefined behavior. You are not allowed to access this string, yet it might be available.

When to release. Instruments says I have a leak

Sometimes I wonder where I would be able to get great information other than from the stack overflow community. I suppose the Objective-C memory management handbook wouldn't be bad, but I feel like you guys can tell me why as opposed to just what to do.
I have the following code:
NSString* rawTickerData = [[NSString alloc] initWithData: op.requestData encoding:NSUTF8StringEncoding];
NSArray* lines = [rawTickerData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for(NSString* line in lines)
{
NSArray* fields = [line componentsSeparatedByString:#","];
if([fields count] > 1)
{
[self.tickerData addObject:fields];
}
}
[rawTickerData release]
Instruments tells me that fields is leaking, but trying to release it after the if statement or doing an autorelease gives an EXC_BAD_ACCESS.
The same thing happens for lines. Releasing it anywhere or trying to do autorelease gives EXC_BAD_ACCESS or "trying to double free" (for autorelease).
Yet, instruments is still saying that these are leaking. Am I missing something?
Unless I'm missing something, the code you've posted is correct. fields is an autoreleased object that gets retained when added to self.tickerData. lines is also autoreleased, so it isn't leaking (at least not in the code shown).
If you're leaking anywhere, it's because you're not properly cleaning up self.tickerData. If you comment out the [self.tickerData addObject:fields]; line, are you still getting leaks reported? If not, make sure you're calling [tickerData release] (or something similar, like self.tickerData = nil) somewhere, probably in your dealloc implementation.
In these cases the best practice is to release your object's at the end of each loop, if you retained them. In this case your fields variable gets an autorelease object, the best option is to use an NSAutoReleasePool to release your objects at the end of the loop.
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
NSString* rawTickerData = [[NSString alloc] initWithData: op.requestData encoding:NSUTF8StringEncoding];
NSArray* lines = [rawTickerData componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for(NSString* line in lines)
{
NSArray* fields = [line componentsSeparatedByString:#","];
if([fields count] > 1)
{
[self.tickerData addObject:fields];
}
[pool drain];
}
[rawTickerData release];
This will flushes all the autoreleased objects if they are don't needed. If you dont use autorelease pool, your objects are living until your method terminates.
If this doesn't resolve your leak problem i suggest to initialize your array like this:
NSArray *lines = [[NSArray alloc]initWithArray:[line componentsSeparatedByString:#","]];
//and release at the end of the loop
[lines release];

Objective-C memory management: how do you release the memory when you return an allocated instance?

How do you release the memory in this situation? Do you have to?
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
// ....
// more code ...
// ...
return mutableString;
}
With autorelease
- (NSString *) whatHappensHere {
NSMutableString * mutableString = [[NSMutableString alloc] initWithString:#"Hello"];
[mutableString autorelease];
return mutableString;
}
As willcodejavaforfood said, the convention is that any newly-allocated object returned from a method should be autorelease'd before being returned. This tells the Objective-C runtime that if nobody takes ownership of it with retain before (in most cases) the current iteration of the application event loop ends, it should be freed.
If it's just used locally in the calling function or returned up the stack, that works great and it gets freed sooner or later. If someone ends up wanting to keep it around, then they have to retain it and so will know they have to release it themselves later on.
(In fact, most of the non-init* utility constructors for ObjC base classes like strings, etc, do just that, or at least something functionally equivalent.)

Retain Count & Copy In Setter?

This is a followup question from a previous question, which is hopefully a little clearer. I am just curious how the code presented below is working, specifically is the variable myString getting released. It does not look like it is from the output?
CODE
// IMPLEMENT
#implementation CelestialBody
- (void)setName:(NSString *)newName{
if(name != newName) {
[name release];
name = [newName copy];
}
}
- (void)dealloc{
[name release];
name = nil;
[super dealloc];
}
#end
.
// ------------------------------------------------------------------- **
// MAIN: 30th September 2009
// ------------------------------------------------------------------- **
#import <Foundation/Foundation.h>
#import "CelestialBody.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
CelestialBody *newPlanet = [[CelestialBody alloc] init];
NSString *myString = [[NSString alloc]initWithFormat:#"go home"];
NSLog(#"RetainCount_1: %d",[myString retainCount]);
[newPlanet setName: myString];
NSLog(#"RetainCount_2: Incremented by copy in setName");
// Clean up
NSLog(#"RetainCount_2: %d -Before Release",[myString retainCount]);
[newPlanet release];
[myString release];
[pool drain];
NSLog(#"RetainCount_1: %d -After Release",[myString retainCount]);
return 0;
}
// ------------------------------------------------------------------- **
OUTPUT
Running…
2009-10-01 09:28:50.395 RetainCount_1: 1
2009-10-01 09:28:50.399 RetainCount_2: Incremented by copy in setName
2009-10-01 09:28:50.399 RetainCount_2: 2 -Before Release
2009-10-01 09:28:50.400 RetainCount_1: 1 -After Release
Debugger stopped.
I am currently re-reading the Memeory Management Guide to try and see what I have missed.
many thanks
EDIT
Just added a the release to the dealloc, It looks like that was what I was missing.
- (void)dealloc{
[name release];
name = nil;
[super dealloc];
}
gary
is the variable myString getting released.
[myString release];
All signs point to yes.
It does not look like it is from the output?
NSLog(#"RetainCount_2: %d",[myString retainCount]);
[myString release];
Your NSLog statement's output doesn't reflect the release message because the release message hasn't happened yet.
Also, don't worry about retain counts. They can be very misleading. As long as you follow Cocoa's rules and don't create any ownership cycles (A owns B owns C owns A), you'll rarely have a problem.
This is not an answer to your question, per se, but an explanation of what you're seeing: The last call to retainCount is sent to a deallocated object, which is undefined behavior. The object happens not to have been overwritten yet, so it still kindasorta "works" in the sense that the method dispatch can still see the old data that's there and doesn't realize it's invalid. You will never get back 0 from calling retainCount, because such an object can't exist.
I guess [newName copy] doesn't actually copy the NSString, because NSString is immutable? I never thought about this, but it makes sense to me.
At the end of your program you're releasing newPlanet and myString. My question is, are you releasing the instance variable name in the -dealloc method of CelestialBody? If you're not, then I believe you're leaking memory there.
Create myString: the NSString's retainCount is 1
[newPlanet setName:]: the NSString's retainCount is 2
[newPlanet release]: the NSString's retainCount is 2 ? I guess you're not releasing it in -dealloc.
[myString release]: the NSString's retainCount is 1

Why does NSSet objectEnumerator increment the retain count?

After getting the objectEnumerator in the following code, the set1 retain count goes to 3. I was surprised to see that because I didn't expect it to change. I searched the documentation and can't find where this effect is explained.
I assume the extra retains are probably set to autorelease by the Cocoa enumeration logic and won't really have any effect in the current event loop. It makes sense the objectEnumerator logic would need a reference to set1 but I'd like to know why they were made. Here is the reason: if I assume set1 has retain count zero after the release in the code then I could try to reuse it another new set. Wouldn't that cause problems since set1 is now pointing at a completely different object/address?
For "bonus" points, is there a way of enumerating the autorelease pool see what it actually contains? TIA
#import <Foundation/NSObject.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSString.h>
#import <stdio.h>;
// macro to create an integer number:
#define INTOBJ(v) [NSNumber numberWithInt: v]
int main (int argc, char *argv[])
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
//Make set
NSMutableSet *set1 = [[NSMutableSet alloc] initWithObjects:
INTOBJ(1), INTOBJ(2), INTOBJ(5), INTOBJ(10), nil];
printf("set1 #%lu\n", [set1 retainCount]);
//Get enumerator of the set. This is where the retain count goes to 3:
NSEnumerator *setEnum = [set1 objectEnumerator];
printf("setEnum #%lu\n", [setEnum retainCount]);
printf("set1 #%lu\n", [set1 retainCount]);
//Iterate through the collection:
printf("[");
NSNumber *element;
while ((element = [setEnum nextObject]) != nil)
//do some this with item. printf is just for debugging:
printf(" %i ", [element intValue]);
printf("]\n");
printf("set1 #%lu\n", [set1 retainCount]);
[set1 release];
printf("set1 after release #%lu\n", [set1 retainCount]);
//More logic could go here reusing variable set1 since I assumed retain count = 0
[pool release];
return 0;
}
It's generally not a good idea to rely on the retain count of objects, as it's an internal detail of the framework. Instead make sure your code adheres to the memory management principles, particularly ensuring that retain/new/copy and release/autorelease are balanced.
Presumably, the enumerator is retaining the collection so that it doesn't get deallocated during enumeration. An enumerator without a valid collection to enumerate wouldn't work very well. In fact, the only way for the enumerator to be sure that it will work is to retain the collection it enumerates.
That said, there's really no reason to ever look at the retain count of any object except for debugging a memory leak/double-release problem. As long as you follow the memory management conventions, you should never need to worry about an object's retain count.
Reusing set1 after the release won't cause problems, because the retain count is on the Object referenced by the variable set1, not on the variable itself.