Regarding a memory leak issue - iphone

I am new to iPhone development. I have a memory leak issue in the following code. If anyone knows why it's happening please help me.
for(int i=0;i<size;i++)
{
NSString *CellIdentifier1;
if(universalApp==2)
{
CellIdentifier1 = #"CustomThumbImageTableCell_iphone";
cell = [[[CustomThumbImageTableCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier1] autorelease];
//NSLog(#">>>>> Creating image >>>>>>>>");
cell.thumbImageView = [[CustomImageView alloc] initWithFrame:CGRectMake(4, 4, 83, 101)];
[imgViewArray addObject:cell.thumbImageView];
[cell.thumbImageView release];
}

Moreover, use auto release pool,
for(int i=0;i<size;i++) {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
...
cell.thumbImageView = [[[CustomImageView alloc] initWithFrame:CGRectMake(4, 4, 83, 101)] autorelease];
...
[pool release];
}
And check imgViewArray, as you know, the retain count of an object added to an NSMutableArray is increased by 1.

Related

Trying to find leak of type NSMutableArray. Instruments shows leak in method.

I eliminated all the leaks from my current app. However Instruments constantly tells me that I have a leak in the method shown below.
The leak is of type NSMutableArray and has a size of either 16 or 32 bytes. Yes, I know that's not much but it adds up. Also see it as an academic question that I need to solve to make my code leakless.
+ (id) meterFromDict:(NSDictionary*)dict {
Meter* resMeter = [[Meter alloc] initWithType:[[dict objectForKey:#"MeterBase"] intValue]];
//NSLog(#"dict: %#",dict);
resMeter.volume = nil;
resMeter.sounds = nil;
resMeter.repeats = nil;
resMeter.volume = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"volumeArray"]] autorelease];
resMeter.sounds = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"soundsArray"]] autorelease];
resMeter.repeats = [[[NSMutableArray alloc] initWithArray:[dict objectForKey:#"repeatsArray"]] autorelease];
//NSLog(#"MeterFromDict called and resmeter.repeats count is : %i",[resMeter.repeats count]);
resMeter.bpm = [[dict objectForKey:#"BPM"] floatValue];
return [resMeter autorelease];
}
Without looking at your Instruments output directly I can't tell exactly, but you're writing some redundant code: Try this:
+ (id) meterFromDict:(NSDictionary*)dict {
Meter* resMeter = [[Meter alloc] initWithType:[[dict objectForKey:#"MeterBase"] intValue]];
//NSLog(#"dict: %#",dict);
resMeter.volume = [dict objectForKey:#"volumeArray"];
resMeter.sounds = [dict objectForKey:#"soundsArray"];
resMeter.repeats = [dict objectForKey:#"repeatsArray"];
//NSLog(#"MeterFromDict called and resmeter.repeats count is : %i",[resMeter.repeats count]);
resMeter.bpm = [[dict objectForKey:#"BPM"] floatValue];
return [resMeter autorelease];
}
There's no point in nilling your properties before assigning new values to them.
Also, No point creating new arrays for arrays that you already have. And if you have properly declared your volume, sounds and repeats properties with copy instead of retain.
Try that and see if it works better.

trouble fixing memory leak iphone

i have 3 memory leaks in my application and i don't find how to fix it. I'm kind of new to xcode and objective c. Here is the code i have:
if(sqlite3_prepare_v2( [[DatabaseController sharedDatabaseController] getDb], sqlQueryConverted, -1, &dbStatement, NULL)==SQLITE_OK){
//Run the query
while ( sqlite3_step(dbStatement) == SQLITE_ROW )
{
const char *name = (const char *)sqlite3_column_text(dbStatement, 0);
int courseId = sqlite3_column_int(dbStatement, 1);
const char *location = (const char *)sqlite3_column_text(dbStatement, 2);
const char *date = (const char *)sqlite3_column_text(dbStatement, 3);
//Convert the returnedElement char to string
nameConverted = [[NSString alloc] initWithUTF8String:name];
locationConverted = [[NSString alloc] initWithUTF8String:location];
dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[[Course alloc]initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted] autorelease];
//Add the course to the to a temporary list to remove duplicated items
[tempResults addObject:course];
}
[nameConverted release];
[locationConverted release];
[dateConverted release];
}
I have tried to autorelease it too. This code is being used to filter a search and reload a search display table. If i put the release line in the while statement the application would crash if i type 2 letters. How could i fix this?
Thanks.
EDIT: I've been going back and forth with this problem with no luck. I've come to the conclusion that there's something wrong with Instruments because it's still showing memory leaks. Here's the code as it is today and as i believe should fix the problem:
NSString *nameConverted = [[NSString alloc] initWithUTF8String:name];
NSString *locationConverted = [[NSString alloc] initWithUTF8String:location];
NSString *dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[[Course alloc]initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted] autorelease];
//Add the course to the to a temporary list to remove duplicated items
[tempResults addObject:course];
course = nil;
[course release];
[nameConverted release];
nameConverted = nil;
[locationConverted release];
locationConverted = nil;
[dateConverted release];
dateConverted = nil;
NSLog(#"course retain count %i",[course retainCount]);
NSLog(#"name coverted retain count %i",[nameConverted retainCount]);
NSLog(#"location coverted retain count %i",[locationConverted retainCount]);
NSLog(#"date coverted retain count %i",[dateConverted retainCount]);
The logs are telling me that the retainCount = 0; so i don't understand why there's a memory leak. Can you guys give me some advice?
Thanks again.
You're leaking at each loop. You are only releasing the 3 lasts NSString. Everytime you re-assign a new NSString to your 3 variables (nameConverted, locationConverted, dateConverted) you loose the reference to the NSString objects they are pointing too. That means memory leaking. You only release the 3 last ones of them, when you get out of your While loop.
You are creating a memory block each while loop, and then only releasing it once. So if your while loop runs over 10 times, each string has a retain count of 10 but only released once.
To fix, put your 3 releases inside your while loop like this:
if(sqlite3_prepare_v2( [[DatabaseController sharedDatabaseController] getDb], sqlQueryConverted, -1, &dbStatement, NULL)==SQLITE_OK){
//Run the query
while ( sqlite3_step(dbStatement) == SQLITE_ROW )
{
const char *name = (const char *)sqlite3_column_text(dbStatement, 0);
int courseId = sqlite3_column_int(dbStatement, 1);
const char *location = (const char *)sqlite3_column_text(dbStatement, 2);
const char *date = (const char *)sqlite3_column_text(dbStatement, 3);
//Convert the returnedElement char to string
nameConverted = [[NSString alloc] initWithUTF8String:name];
locationConverted = [[NSString alloc] initWithUTF8String:location];
dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[[Course alloc]initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted] autorelease];
//Add the course to the to a temporary list to remove duplicated items
[tempResults addObject:course];
[nameConverted release];
[locationConverted release];
[dateConverted release];
}
}
Have you tried to use:
nameConverted = [[NSString initWithUTF8String:name] autorelease];
locationConverted = [[NSString initWithUTF8String:location] autorelease];
dateConverted = [[NSString initWithUTF8String:date] autorelease];
And removing the releases outside the loop?
Also do not autoremove the course object, release it after adding it to tempResults.
This is how you should be doing it.
NSString *nameConverted = [[NSString alloc] initWithUTF8String:name];
NSString *locationConverted = [[NSString alloc] initWithUTF8String:location];
NSString *dateConverted = [[NSString alloc] initWithUTF8String:date];
Course *course = [[Course alloc] initWithName:nameConverted _id:courseId location:locationConverted courseDate:dateConverted];
[tempResults addObject:course];
[course release];
[dateConverted release];
[locationConverted release];
[nameConverted release];

Set instance variable twice with "self.variable = value" causes memory leak?

Until yesterday I thought that I had understood the iPhones memory management.
Well here is my problem:
// .h file
#property(nonatomic, retain) NSMutableDictionary *dicParams;
#property(nonatomic, retain) NSMutableDictionary *dicReferences;
#property(nonatomic, retain) FtMonitorHandler *monitorHandler;
// .m file
#synthesize dicParams, dicReferences, monitorHandler;
- (id)init {
self = [super init];
if (self) {
self.dicParams = [[NSMutableDictionary alloc] init];
self.dicReferences = [[NSMutableDictionary alloc] init];
self.monitorHandler = [[FtMonitorHandlerService alloc] init];
}
return self;
}
- (void)dealloc {
[monitorHandler release];
[dicParams release];
[dicReferences release];
[super dealloc];
}
If I set somewhere else, after the viewcontroller's allocation for example
self.dicParams = dicValues;
… it will turn into a leak
My understanding of setting instance variables with "self. …" was, that the current value will be "released" and then set with "retain".
I tried a little bit with instruments. Results:
-(void)createLeak {
self.dicParams = [[NSMutableDictionary alloc] init];
self.dicParams = [[NSMutableDictionary alloc] init];
}
-(void)createAnotherLeak {
self.dicParams = [[NSMutableDictionary alloc] init];
self.dicParams = nil;
self.dicParams = [[NSMutableDictionary alloc] init];
}
- (void)createWithoutLeak {
if(dicParams != nil) [dicParams release];
self.dicParams = [[NSMutableDictionary alloc] init];
}
Have I missed something, or is this the behavior as it should be?
EDIT: I tried to implement the suggested changes. It works fine, as long, as my variable is not GUI element. (UIView, UILabel, etc)
The autorelease will cause an app crash after a memory warning
- (void)loadView {
[super loadView];
// ... here is some other stuff ...
self.lblDeparture = [[[UILabel alloc] init] autorelease];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
self.lblDeparture = nil;
}
- (void)dealloc {
[lblDeparture release];
[super dealloc];
}
I'm not quite sure, but I assume that the following lines are the real issue:
CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, INFO_VIEW_HEIGHT);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
[imageView addSubview:lblDeparture];
[lblDeparture release]; // is this correct?
[self.view addSubview:imageView];
[imageView release];
if you init you need to auto release.
-(void)dontCreateAnotherLeak {
self.dicParams = [[[NSMutableDictionary alloc] init] autorelease];
self.dicParams = nil;
self.dicParams = [[[NSMutableDictionary alloc] init] autorelease];
}
the easier equivalent is to use the convenience accessor.
self.dicParams = [NSMutableDictionary dictionary];
if you would like to handle this yourself. On top of the #synthesize dictParams; you will also want to create your own setter.
-(void)setDictParams:(NSMutableDictionary*) newDictParams
{
if (dictParams != newDictParams)
{
[dictParams release];
dictParams = [newDictParams retain];
}
}
this is a little simple. but essentially what the compiler creates with the retain modifier added to the #property tag
If you set a instance variable for which you have specified retain in property retain count becomes 1
Now as you call with reference to self as in case “self.variable = value” increase the retain count by 1, So the total retain count becomes 2.
So now to release it you need to bring retain count to 0. Hence you need to release it twice.
Hopew this helps.
I am not sure I understand the question fully, however your second part is easily explained...
-(void)createLeak {
self.dicParams = [[NSMutableDictionary alloc] init];
self.dicParams = [[NSMutableDictionary alloc] init];
that's clear...
now but this one
-(void)createAnotherLeak {
self.dicParams = [[NSMutableDictionary alloc] init];
self.dicParams = nil;
self.dicParams = [[NSMutableDictionary alloc] init]; }
does not release the first alloced self.dicParams but rather forgets any reference to it by setting it to nil and then resetting it with a new one. Setting to nil is not equal to release. If you would have created the first one with autorelease and then set it to nil it's something different. That should work correctly. And that's exatcly what you do with your 3rd example!
Now as to your inital question, what is it that leaks when you write
self.dicParams = dicValues;
?
the variable self.dicParams should just hold the value until you release it again
I recommend you read Apple's Memory Management Programming Guide carefully. http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
It's all explained in there.
There are a couple of obvious mistakes I can see that you are making.
Firstly, you shouldn't use accessors in init or dealloc.
So, this
- (id)init {
self = [super init];
if (self) {
self.dicParams = [[NSMutableDictionary alloc] init];
self.dicReferences = [[NSMutableDictionary alloc] init];
self.monitorHandler = [[FtMonitorHandlerService alloc] init];
}
return self;
}
should be
- (id)init {
self = [super init];
if (self) {
dicParams = [[NSMutableDictionary alloc] init];
dicReferences = [[NSMutableDictionary alloc] init];
monitorHandler = [[FtMonitorHandlerService alloc] init];
}
return self;
}
Secondly, when you set a retained property, the you need to release the whatever you are setting it to.
So, this
self.dicParams = [[NSMutableDictionary alloc] init];
should be
self.dicParams = [[[NSMutableDictionary alloc] init] autorelease];
or you can do this
NSMutableDictionary *newDicParams = [[NSMutableDictionary alloc] init];
self.dicParams = newDicParams;
[newDictParams release];
The setter generated by #synthesize for a (readwrite, retain, nonatomic) property looks something like this:
- (void) setSomething: (id) newSomething;
{
if (something != newSomething) {
[something release];
something = [newSomething retain];
}
}
The old object pointed to by the instance variable something will be released while the new object will be retained.
Your mistake is the object creation. You create your dictionary with [[NSDictionary alloc] init]. This dictionary has a retain count of 1. Your setter retains the object, so the new retain count is 2. When you call the setter again the retain count of your original dictionary correctly gets decreased - it’s 1 again. For the dictionary to be freed you’d have to release it again. For this there is autorelease. An autoreleased object will get released some time later. So the correct code to set your property would be
self.something = [[[NSDictionary alloc] init] autorelease];
or even better using the convenience allocator
self.something = [NSDictionary dictionary];
You really should read and understand Apple’s memory management guide - it’s all explained in there.
By the way, I talked about retain counts here. It’s OK to think about them, but you should never ask an object about it’s retain count, that value is useless, since it’s rarely what you would think.

Setting dictionary in singleton causing EXC_BAD_ACCESS

I'm having issues with a singleton I've created. It contains two NSMutableDictionary's, which are read and used in three views (and some modal views) throughout the app.
I've added an MKMapView t plot some of the venues inside the dictionaries on a map. When I use the exact same method/function used in every other view to access the data, I receive an EXC_BAD_ACCESS error pertaining to a deallocated dictionary. This comes from NSZombieEnabled:
CFDictionary retain: message sent to deallocated instance
In a dsym'ed trace, it is the replacement of one dictionary with another that is causing grief. The code I'm using to call the function comes from a MKAnnotationView click:
UIControl *tempButton = sender;
NSString *selectedEventsString = [self.eventsArray objectAtIndex:tempButton.tag];
NSLog(#"eventString: %#", selectedEventsString);
[[EventsManager eventsManager] changeSelectedEventsDictionaryTo:selectedEventsString];
[tempButton release];
[selectedEventsString release];
"selectedEventsString" is coming out to a perfectly corresponding event.
The corresponding code in EventsManager:
-(void)changeSelectedEventsDictionaryTo:(NSString *)eventName {
NSLog(#"singleton: %#", eventName);
self.eventString = eventName;
self.selectedEventsDictionary = [self.eventsDictionary objectForKey:eventName];
}
Both selectedEventsDictionary and eventsDictionary are set as #property (nonatomic, retain) in the .H file, and this is the init function:
+ (EventsManager*)eventsManager {
if (eventsManager == nil) {
eventsManager = [[super allocWithZone:NULL] init];
eventsManager.eventsDictionary = [[NSMutableDictionary alloc] init];
eventsManager.selectedEventsDictionary = [[NSMutableDictionary alloc] init];
eventsManager.eventString = [[NSString alloc] init];
eventsManager.mode = [[NSString alloc] init];
}
return eventsManager;
}
This is an example of code used in other views that works fine:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
NSString *eventString = [self.eventsArray objectAtIndex:row];
[[EventsManager eventsManager] changeSelectedEventsDictionaryTo:eventString];
//Modal display code here
}
Any help would be greatly appreciated! I think I've provided all relevant code but let me know if more is needed.
Cheers!
Where to start! I will point out some things that I do see wrong.
First Example. Do not release tempButton and selectedEventString as you never explicitly called retain/copy or alloc and init on them.
UIControl *tempButton = sender;
NSString *selectedEventsString = [self.eventsArray objectAtIndex:tempButton.tag];
NSLog(#"eventString: %#", selectedEventsString);
[[EventsManager eventsManager] changeSelectedEventsDictionaryTo:selectedEventsString];
//DO NOT RELEASE THESE YOU NEVER RETAINED THEM!
[tempButton release];
[selectedEventsString release];
Your static eventsManager is not thread safe which may not be a issue for you but should definitely be looked into.
Read the comments for the following code example
+ (EventsManager*)eventsManager {
if (eventsManager == nil) { //<-- Not thread safe
//DO NOT CALL SUPER USE self
//eventsManager = [[self alloc] init];
eventsManager = [[super allocWithZone:NULL] init];
//You need to autorelease these values or use an autoreleased static method
//eventsManager.eventsDictionary = [NSMutableDictionary dictionary];
//eventsManager.selectedEventsDictionary = [NSMutableDictionary dictionary];
eventsManager.eventsDictionary = [[NSMutableDictionary alloc] init];
eventsManager.selectedEventsDictionary = [[NSMutableDictionary alloc] init];
//Do not bother setting these at all or just set them to nil
eventsManager.eventString = [[NSString alloc] init];
eventsManager.mode = [[NSString alloc] init];
}
return eventsManager;
}
Make sure all of those properties are set to retain or copy and that may fix your problem. If you still have an issue after these fixes you can update your question and I will update my answer.

UIImage & UIImageView memory leak

i'm testing my app on Instruments,
and I see that my UIImage and UIImageView are memory-leaking like crazy...
I'm basically using recursion, so same variables get to load different images on each call.
nextImageName = [[NSString alloc] init];
nextImageName2 = [[NSString alloc] init];
nextImageName = [[currentPlayers objectAtIndex:playerIndex] retain];
nextImageName2 = [[currentPlayers objectAtIndex:(playerIndex+1)] retain];
nextImage = [[UIImage alloc] init];
nextImage2 = [[UIImage alloc] init];
nextImage = [UIImage imageNamed:nextImageName];
nextImage2 = [UIImage imageNamed:nextImageName2];
nextImageView = [[UIImageView alloc] init];
nextImageView2 = [[UIImageView alloc] init];
nextImageView = [[UIImageView alloc] initWithImage:nextImage];
nextImageView2 = [[UIImageView alloc] initWithImage:nextImage2];
NSLog(#"r:%d",currentRound);
NSLog(#"%d vs. %d", playerIndex, playerIndex+1);
buttonOne = [[UIButton alloc] init];
buttonTwo = [[UIButton alloc] init];
playerOne = nextImageView;
playerTwo = nextImageView2;
playerOne.frame = CGRectMake(180.0, 200.0, 275.0, 275.0);
playerTwo.frame = CGRectMake(550.0, 200, 275.0, 275.0);
buttonOne.frame = CGRectMake(180.0, 200.0, 275.0, 275.0);
buttonTwo.frame = CGRectMake(550.0, 200.0, 275.0, 275.0);
[buttonOne addTarget:self action:#selector(announceWinner:)
forControlEvents:UIControlEventTouchUpInside];
[buttonTwo addTarget:self action:#selector(announceWinner2:)
forControlEvents:UIControlEventTouchUpInside];
Could anyone please help me? This is driving me nuts..
I originally had release for all the variables at dealloc, but it seemed it didn't go into dealloc, so I also put it in viewDidUnload and didReceiveMemoryWarning.
I think the problem is this:
"I'm basically using recursion, so same variables get to load different images on each call"
...coupled with this:
"I originally had release for all the variables at dealloc, but it seemed it didn't go into dealloc, so I also put it in viewDidUnload and didReceiveMemoryWarning"
So essentially, if I understand your code correctly, you were making several passes through the alloc/init section over the lifetime of your class, but only ever calling release once, when the class itself is deallocated. I would expect that to leak like crazy.
You should be able to fix it by changing the alloc/init section to follow a pattern like:
if (nextImageName) {
//if it was previously set, release it so that the old instance doesn't leak
[nextImageName release];
}
nextImageName = [[NSString alloc] init];
if (nextImageName2) {
[nextImageName2 release];
}
nextImageName2 = [[NSString alloc] init];
//and so on...
This assumes that these variables are all declared as instance-variables on your class, and that in init you set them all to nil, like so:
- (void) init {
if ((self = [super init])) {
//set default values
nextImageName = nil;
nextImageName2 = nil;
//and so on...
//do other setup things here
}
}
nextImageName = [[NSString alloc] init];
nextImageName = [[currentPlayers objectAtIndex:playerIndex] retain];
there you have your first memleak.. you are allocating a string and then you replace that object with the one one from the array.
you can simply remove the first line.. you don't have to allocate a object before getting one out of an array.
But without seeing more code we can't see what you are releasing later.. I hope you are releasing stuff later ;)
You are allocating and init'ing each object and then increasing the retain count again.
Anytime you call alloc, copy, new or retain...the retain count of the object goes up by 1. When you release, the retain count goes down by 1. So when you type
nextImageName = [NSString alloc] init];
and then
nextImageName = [[currentPlayers objectAtIndex:playerIndex] retain];
nextImageName now has a retain count of 2. If you only used the second line, you would be fine. You don't need to allocate a new object, you just need to retain the one that's being returned to you.
Likewise nextImageView and nextImageView2 are being allocated twice and therefore have a retain count of 2. You only need the second call you're making.
nextImageView = [UIImageView alloc] initWithImage:nextImage];
Hope this helps.
Try this code. It will clear the memory leaks on UIImage and UIImageView.
nextImageName = [NSString string];
nextImageName2 = [NSString string];
nextImageName = [[currentPlayers objectAtIndex:playerIndex] retain];
nextImageName2 = [[currentPlayers objectAtIndex:(playerIndex+1)] retain];
nextImage = [UIImage imageNamed:nextImageName];
nextImage2 = [UIImage imageNamed:nextImageName2];
nextImageView = [[UIImageView alloc] initWithImage:nextImage];
nextImageView2 = [[UIImageView alloc] initWithImage:nextImage2];
NSLog(#"r:%d",currentRound);
NSLog(#"%d vs. %d", playerIndex, playerIndex+1);