Consider the following method, where I build a string and return it. I would like to release the building blocks of the string, but then the string is based on values that no longer exists. Now what?
Am I leaking memory and if so, how can I correct it?
- (NSString) getMiddahInEnglish:(int)day{
NSArray *middah = [[NSArray alloc] initWithObjects:#"Chesed", #"Gevurah", #"Tiferes", #"Netzach", #"Hod", #"Yesod", #"Malchus"];
NSString *firstPartOfMiddah = [NSString stringWithFormat: #"%#", [middah objectAtIndex: ((int)day% 7)-1]];
NSString *secondPartOfMiddah = [NSString stringWithFormat: #"%#", [middah objectAtIndex: ((int)day / 7)]];
NSString *middahStr = [NSString string#"%# She'bi#%", firstPartOfMiddah, secondPartOfMiddah];
[middah release];
[firstPartOfMiddah release];
[secondPartOfMiddah release];
return middahStr;
}
At the end of the method, the return string, middahStr has not been released. Is this a problem? Am I leaking memory?
Quite the opposite: You are over-releasing.
middah you alloc and init. Then you release it. All is well.
firstPartOfMiddah and secondPartOfMiddah, you call an NSString "stringWith" convenience method. This is equivalent to invoking alloc, init, and autorelease. Your releasing them is a mistake, as they are essentially given to autorelease to release later.
middahStr you call the convenience method, but return it. All is well. It will be released later by the autorelease pool.
Rule of thumb (I'm sure there are plenty of exceptions): If you don't alloc it, don't release it.
Related
str is a NSString I own. It is my responsibility to release it,
since I call initWithString:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
str = [str stringByAppendingString:#"\n"];
}
if the line inside the if statement reached, I will lose the ownership of the str var.
so my app crashes with a zombie instance, when I release str later:
[str release];
All goes fine if the if statement is NO(false).
What can I do to maintain the ownership of str?
Note that str could be very long I don't want to init another NSString
You need to do normal memory management:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
NSString *newStr = [str stringByAppendingString:#"\n"];
[str release];
str = [newStr retain];
}
Keep in mind that stringByAppendingString: returns an autoreleased string (and it also creates a whole new string).
Suggestion 1: Use ARC. It solves these problems for you. This is by far the best solution.
As rmaddy says, Xcode has an automated tool for converting apps to ARC. Look in the edit menu, under refactor>Convert to Objective-C ARC. The process is fairly painless. It flags things it wasn't able to figure out on it's own (usually only a few things.) After you clean up those issues you are off and running and never have to worry about retain counts again.
Suggestion 1a: Make str a mutable string, as #rmaddy suggested.
Then your code would look like this:
[str appendString: #"\n"];
That's simpler, easier to read, more memory-efficient, and works exactly the same in both ARC and manual reference counting.
Failing that, change str to be a retained property
#property (nonatomic, retain) NSString *str);
Then use property notation:
if (![[self.str substringFromIndex: self.str.length-1] isEqualToString:#"\n"])
{
self.str = [self.str stringByAppendingString:#"\n"];
}
When you do that the setter for the property takes care of releasing the old object in the str property before assigning a new value to the property.
Be aware, though, that assigning an object to a retained property increases it's retain count. This will create a leak:
self.str = [NSString alloc] initWithFormat: #"number %d", value];
(because all alloc/init calls return objects with a retain-count of 1)
and then the property retains it again.
That code should be written like this:
self.str = [[NSString alloc] initWithFormat: #"number %d", value] autorelease];
I have a big NSDictionary with a smaller NSDictionary inside. I want to autorelease the bigger one, and retain the second. I have this code in my init method:
// Autoreleased stage dictionary
NSString *plistPath = [[NSBundle mainBundle] pathForResource:map ofType:#"plist"];
NSDictionary *mapDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
// Owned items
citiesDictionary = [[mapDict objectForKey:#"Cities"] retain];
citiesDictionary is declared in the class interface as an:
NSDictionary *citiesDictionary;
If I try to release citiesDictionary in dealloc using the following line it crashes.
[citiesDictionary release];
But if I don't dealloc citiesDictionary, I get a memory leak reported in Instruments when I dealloc the class containing citiesDictionary. I acknowledge that mapDict is being deallocated at the end of init. Does this deallocation affects citiesDictionary even though I called retain on it?
If so, how can I keep the smaller dictionary while freeing the bigger containing one? I tried different things when assigning citiesDictionary, but nothing seems to work correctly. Some of the approaches I tried:
citiesDictionary = [[mapDict objectForKey:#"Cities"] copy];
and
citiesDictionary = [NSDictionary initWithDictionary:[mapDict objectForKey:#"Cities"]];
and even
citiesDictionary = [NSDictionary initWithDictionary:[[mapDict objectForKey:#"Cities"] copy]];
Copy the dictionary to citiesDictionary.
NSString *plistPath = [[NSBundle mainBundle] pathForResource:map ofType:#"plist"];
NSDictionary *mapDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
citiesDictionary = [[mapDict objectForKey:#"Cities"] copy];
Now you can be sure to have copy of the dictionary even though the containing object can be released at any time. Remember then to also subsequently release the citiesDictionary.
Note when using copy that you will always get an immutable NSDictionary even if the original object was an NSMutableDictionary. Use mutableCopy to obtain an NSMutableDictionary if needed.
You're overreleasing citiesdictionary somewhere, seeing you have already called retain in the init method. Map dict will only release your citiesdictionary once when it is autoreleased, but will not cancel the retain you have done in the init method.
You should try to check your other methods for a release statement to your dictionary, or have the static analyzer tell you.
It was completely my fault: I was inserting a memory-management flawed custom class inside a NSMutableDictionary related to the citiesDictionary, and freeing that other thing was causing the memory to go corrupt.
Now, both
citiesDictionary = [[mapDict objectForKey:#"Cities"] release];
and
citiesDictionary = [[mapDict objectForKey:#"Cities"] copy];
work perfectly fine.
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];
I'm a little confused about Objective-C and allocating/releasing objects.
If I do this:
NSString *myString;
if([someString isEqualToString: #"test1"]){
myString = #"got 1";
}else{
myString = #"got 2";
}
Do I have to release myString after that?
And the same with self-defined objects:
myOwnObject *someObject = [someArray objectAtIndex: 1];
myButton.label1.text = someObject.name;
Do I have to release someObject?
The reason why I'm asking is that I get memory-leaks in a method and I can't find where it is. So I'm trying to figure out whether I do the alloc/release stuff correctly.
The leak occurs on a NSPlaceholderString (I guess that's somewhere hidden in my NIB-File).
Also - if I have an object, allocate it, but only use some of the properties, but DO a release of every property on dealloc - will this cause memory leaks?
Sorry - hope my questions do make at least some sense :)
Thanks for any help!
Listen to me. THIS IS THE ONLY RULE THAT MATTERS.
If you use a method with "copy", "alloc", "new", or "retain" in the name
You own the object and MUST later release or autorelease it.
If you don't:
Don't!
But don't expect the object to stick around outside of that scope, because you don't own it.
It's that simple.
MyClass *foo = [[MyClass alloc] init];
[array addObject:foo];
[foo release];
Did you use "copy", "retain", "new", or "alloc"? Yes. Release it.
MyClass *someObject = [someArray objectAtIndex:0];
Did you use "copy", "retain", "new", or "alloc"? No. Don't release it.
BUT
If you have an instance variable which you need to access in other methods:
ivar = [[someArray objectAtIndex:0] retain];
Then you're guaranteed it will stick around because you own it.
(Another way to handle this is with #property (retain) properties, because then you can do self.ivar = someObject and it'll retain it for you.)
But remember to release them in -dealloc!
No, you don't have to release either of those. I usually release only objects that I alloc, such as this snippet:
NSString *string = [[NSString alloc] initWithFormat:#"%#", something];
// yadda yadda yadda some code...
[string release];
To answer your first question, you don't need to release strings created with the #"" syntax.
On your second example, you should not have to release someObject. However, a problem could arise if your dealloc method in your myOwnObject class does not correctly release all of its instance variables.
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.