Leak or Crash - difference between autorelease and release - objective-c

I have a comprehension question. This method is given:
- (NSArray*)test {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://stackoverflow.com/"]];
NSString *result = [[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSMacOSRomanStringEncoding];
result = [result stringByAppendingString:#"something"];
NSArray *arr = [NSArray arrayWithObject:result];
//[result release];
return arr;
}
If I uncomment the release the App would crash and say it cannot access a released object.
By not releaseing the result string Instruments would report a leak (NSPlaceholderString).
I can autorelease it on the same line I alloc it, that would solve the problem (which I'm currently doing in my App).
If I understand it correctly stringByAppendingString: should create an autoreleased object so the 'old' result could be deallocated. Then the method arrayWithObject: should copy the object into an array. So my thought was to release the string after it was copied to the array.
Am I missing something or something wrong with my knowledge?

Let's go through your code line by line.
- (NSArray*)test {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://stackoverflow.com/"]];
This creates a data object. You don't own it, but it will stick around for the rest of the method's time. So far, so good.
NSString *result = [[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSMacOSRomanStringEncoding];
This creates a string object that you own. Again, no problem here — we just need to release it later.
result = [result stringByAppendingString:#"something"];
You throw away your reference to the string object that was in result and store a new string object that you do not own. This is a leak because you can no longer release the original string. Also, you're correct in noting that the new string can be treated as an autoreleased object — which means you should not release it.
NSArray *arr = [NSArray arrayWithObject:result];
Contrary to your belief, this does not copy anything. It merely keeps a reference to the new string and retains it.
//[result release];
You should not release result at this point, because the object it contains is not one you own — you got it from stringByAppendingString:, not from a method with new, alloc, retain or copy in its name. Releasing this object that you do not own will almost certainly result in a crash at some point. The old object that you own and should release was lost two lines earlier, and releasing something else in its place won't help.

result = [result stringByAppendingString:#"something"];
This line replaces the first allocated string by a new autoreleased string.
So the first string is leaked and the second one should not be released. This explains why uncommenting the release line crashes.

Related

Is this a correct situation to call release on an object in Objective-C?

I'm new to Objective-C and I am having some difficulty with understanding memory management.
So let's say I have a class and an instance variable of type NSString* that isn't tied to a property. Would this situation leak memory?
myString = [[NSString alloc] init];
//more program stuff
myString = [[NSString alloc] init];
Should I have called [myString release] before I set myString equal to a new object? Will this sort of code leak memory? Do I have to release an object like this every time I have the pointer point to a different object?
First, Apple's Memory Management Programming Guide is a great place to look for examples and instructions about memory management.
Now to your question. When you call myString = [[NSString alloc] init]; you are reassigning the pointer myString and as such lose access to the original NSString, thus creating a memory leak.
The general rule of thumb here is that for every alloc you should have a release and these must alternate appropriately. If you do
myString = [[NSString alloc] init];
// ...
myString = [[NSString alloc] init];
// ...
[myString release];
[myString release];
you are releasing the same instance twice which results in overreleasing and an error BAD-ACCESS. The correct thing to do is
myString = [[NSString alloc] init];
// ...
[myString release];
myString = [[NSString alloc] init];
// ...
[myString release];
so that each instance is correctly released.
For future, stick to the simple rule that for every alloc/copy/retain you should pair it with a release.
Yes this will leak.
By allocating and assigning another NSString instance to the same variable myString, you lose reference to the original content of myString and therefore the ability to send it a release message.
Both, the Clang static analyzer and the Leaks instrument should uncover this leak.
Yes, you need to release the first object before you reassign your variable to point to the second object. Otherwise, you lose track of your first object and you leak it because you can't release it. You are responsible for releasing the object because you created it (via alloc) as explained in the Memory Management Rules.
Also, bear in mind that [[NSString alloc] init] is creating an empty and immutable string (NSStrings are immutable meaning that they can not be changed after creation). There is little point in doing this.

init, alloc and retain and "the initial value is never read"

Ive have used the static analyser to look through my code and have arrived at the following question.
NSString *string = [[NSString alloc] init];
string = [object name];
This give me a memory error saying that the initial value is never read.
I replaced it with the following
NSString *string = [[object name]retain];
Is this better/correct coding?
Cheers
This code:
1: NSString *string = [[NSString alloc] init];
2: string = [object name];
is incorrect because in line 1: you allocate new memory and store reference to it in variable string. In line 2: you store in variable string reference to another memory location.
As a result, you didn't read memory value that was allocated at line 1: and even didn't release it. So moreover you have memory leak there.
If you want to save reference to some place in memory you don't need alloc+init. You should use alloc+init when you want to allocate some space in memory where you will write data or read from.
Your variable string is actually a pointer to an NSString object. The first line of your code created a new empty string and assigned a pointer to it to string. The second line of code then immediately overwrites that pointer with a pointer to a completely different string. You have never read the original value, and there is no way to access the allocated NSString, thus it has leaked.
The second option is correct., provided you release/autorelease it somewhere later.
I have seen someone else do this exact thing. NSString *string = [[NSString alloc] init]; creates a new object and assigns it to string. string = [object name]; Assigns the name of object to string. It is similar to saying int a = 0; a = 4, 0 has no effect on the 4. The problem with your code is that [[NSString alloc] init] creates a new object with a retain count of 1, because you do not release it it leaks. [object name] returns an autoreleased object which will disappear at the end of the runloop.
In short, use NSString *string = [[object name] retain];

String splitting problem

I am work on a simple program in which I split a string and a user global, I use the following code for splitting the string.
NSString *GlobleStr;//globale variable
//===============
NSString *xmlParsingResult=#"Apple,iphone";
NSArray *array = [xmlParsingResult componentsSeparatedByString:#","];
NSString *StrResult = [NSString stringWithFormat:#"%#", [array objectAtIndex:0]];
GlobleStr =[NSString stringWithFormat:#"%#",[array objectAtIndex:1]];
NSLog(#"cmd %#",StrResult);
NSLog(#"value%#",GlobleStr);
my code can split the string and o/p is cmd:Apple value:iphone
but my problem is that as soon as I call another xib then my global variable will be empty or nil and the application will crash ( it throws error like Variable is not cfstring).
Any suggestions?
It's because NSString's +stringwithFormat: method returns an autoreleased string. In a local variable this is often what you want to prevent memory leaks (otherwise you have to manually release the string when you're done with it). The problem here is that the string in GlobleStr is getting released by the autorelease pool sometime after you assign it, then when you try to access it in another place you get a crash.
The fix is this: GlobleStr = [[NSString stringWithFormat:#"%#",[array objectAtIndex:1]] retain];
As an aside, you can just do this instead:
GlobleStr = [[array objectAtIndex:1] retain];
I strongly recommend reading Apple's documentation regarding memory management in Cocoa: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html .
Finally, without seeing your code I can't say for sure, but I'd be curious to know why you're using a global variable for GlobleStr. It's a blanket statement, and there are certainly exceptions, but when programming in Cocoa there's probably a better way to structure your code.
You need to retain your global, otherwise it will be deallocated when the autorelease pool drains:
GlobleStr = [[NSString stringWithFormat:#"%#", [array objectAtIndex:0]] retain];
Remember to release it later on when you're done -- in particular, before assigning any other value to it.

NSString and NSData Memory Management

I have the following code for creating an NSString to contain the body of the text file and then convert it to NSData and output it to a file.
NSString *particleString = [[NSString alloc] initWithFormat:#"%#", #"This is the body of my file"];
NSData *metaVals = [particleString dataUsingEncoding:NSISOLatin1StringEncoding];
Since I have created particleString using alloc, I thought I needed to release it after I have finished converting it to NSData, hence I added
[particleString release];
But my app crashes when I add this line. However, when I remove the line where I use it to create metaVals, I can safely release it.
Can anyone explain why passing it to NSData stops me from releasing it? I believe I own particleString, what's going on?
According to your comment you do particleString = [particleString stringByAppendingFormat:#"%#", #"Some other string"]; which looses the reference to the original particleString, and replaces it with an autoreleased version. You then go on to release the autoreleased version, causing both a leak of the original particleString, and an overrelease of the new one.
Try this
NSString *particleString = [NSString stringWithFormat:#"%#", #"This is the body of my file"];
particleString = [particleString stringByAppendingFormat:#"%#", #"Some other string"];
NSData *metaVals = [particleString dataUsingEncoding:NSISOLatin1StringEncoding];
It no longer has a release because both strings are now autoreleased.
I would suggest re-reading the memory management rules
[particleString stringByAppendingFormat:#"%#", #"Some other string"];
this returns a newly created object. which means the first object created and assigned to particleString is leaking, and you are trying to release the newly, autoreleased, object that has been created by using the -stringByAppendingFormat: method
You could probably use a NSMutableString instead which has a method named
appendFormat:(NSString *)...

Releasing of strings

I got a doubt that when should the strings are released.I am not made any allocation to the string is there any necessary to release the string?
No if you do not "allocate" the string they are auto released.
for example
NSString *aTestString = [NSString stringWithFormat:#"Hello %#",#"World"];
This string is auto released, so you do not have to call [aTestString release];
If you would do:
NSString *aTestString = [[NSString alloc] initWithFormat:#"Hello %#",#"World"];
Then you would need to release it by [aTestString release]; because you manually allocated.
Therefore it is wise to autorelease it, so you do not have to think of it later on
NSString *aTestString = [[[NSString alloc] initWithFormat:#"Hello %#",#"World"] autorelease];
But that would just be the same as the first piece of code I gave ya.
Back to the point, no you do not have to manually release it as long as you do not allocate it yourself.
Did you create the string via a call to alloc, new, or a method containing copy? Did you explicitly retain the string yourself? If you got the NSString from a CFStringRef, did you create the CFStringRef with a function that included create? If not, you don't have to do anything. If you did, you have to either release or autorelease the string.
Object allocation/deallocation rules
You need to call [Object release] if and only if:
You called [Object alloc]
You called [Object retain]
You called [Object new]
If you did not explicitly allocate or retain the object, then you need to release it. If you got the object via a class method, the method did something like this: return [[[Object alloc] init] autorelease];. This allocates a new object, but is autoreleased when the NSAutoReleasePool next gets a chance.