Memory Leak the second time the view loads - iphone

i been cracking my head over this memory leak..
my datasource is mutabledictionary..that i load in the viewdidload. if i dont retain it. i dont have access it it in cellforrowatindexpath. but when i retain it.. it shows up as a memory leak in instruments. i have tried so many different variations.. doesnt seem to get it right.
here is the code the leak is in "dict" and "plistPath"
`
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.navigationBarHidden = NO;
self.title = #"Messages & Lists";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[plistPath release];
plistPath = [documentsDirectory stringByAppendingPathComponent:#"general.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[dict release];
if ( [fileManager fileExistsAtPath:plistPath] ) {
dict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath] ;
} else {
dict = [NSMutableDictionary dictionaryWithCapacity:1];
[dict setObject:#"NO" forKey:#"busyStatus"];
[dict setObject:#"NO" forKey:#"replyToAll"];
[dict setObject:#"NO" forKey:#"replyToList"];
[dict setObject:#"NO" forKey:#"dontReplyToList"];
[dict writeToFile:plistPath atomically:YES];
}
[tableData release];
tableData = [[NSMutableDictionary alloc] init];
[tableData setObject:[NSArray arrayWithObjects:#"Help",#"Set Default Message",#"Reply To All",[dict objectForKey:#"replyToAll"],nil] forKey:#"1"];
[tableData setObject:[NSArray arrayWithObjects:#"Reply to a List",[dict objectForKey:#"replyToList"],#"List of Contacts",nil] forKey:#"2"];
[tableData setObject:[NSArray arrayWithObjects:#"Don't reply to List",[dict objectForKey:#"dontReplyToList"],#"List of Contacts",nil] forKey:#"3"];
[dict retain];
[plistPath retain];
}
`
there is no leak the first time the view loads. but if i got back. and then load the view again it leaks.
thanks in advance for anyone who can help me out.

You have to call [dict release] in your view controller's dealloc method.

I think you should release your dictionary in the dealloc method of your view controller so that the retain count of your datasource is decreased when the ViewController is deallocated.
Retaining without releasing after use is the main source of memory leak.

It seems right.
The only thing that confuses me is the tableData. Is that var released or allocated somewhere else too?
Say you do allocate it somewhere else, and not releases it in dealloc, shouldn't these be the steps then:
tableData is allocated somewhere else.
tableData is released in viewDidLoad
tableData is allocated again (counter 1).
Exiting view, entering view again, tableData allocated again, released and allocated in function (counter two?)
Seems like a longshot, but. Can you display what the instruments say?

Related

Leak in NSMutableArray

I have been pulling out my hair trying to figure out why this is leaking. In my .h file I have a synthesized property nonatomic, retained NSMutableArray. In my viewDidLoad I declare it as:
self.tableData = [[NSMutableArray alloc] init];
[self.tableData removeAllObjects];
fillData(self.tableData);
Throughout my application, I call [self.tableData removeAllObjects] and then repopulate it with the fillData(self.tableData) function. This function fills up the data from a static C++ string set:
void fillData(NSMutableArray* list)
{
for (set<string>::const_iterator itr = sortedData.begin(); itr != sortedData.end(); ++itr){
[list addObject:[NSString stringWithFormat:#"%s", ((string)*itr).c_str()]];
}
}
In my dealloc method I do:
[self.tableData removeAllObjects], [self.tableData release], tableData = nil;
Where did I drop the ball? Instruments says it's in the [list addObject....] line.
Thanks
self.tableData = [[NSMutableArray alloc] init];
[self.tableData removeAllObjects];
fillData(self.tableData);
+1 retain for alloc, +1 retain for using the property's setter. You haven't balanced the +1 from alloc. If you are going to use the setter:
self.tableData = [NSMutableArray array];
fillData(self.tableData);
Note that removeAllObjects in that is completely pointless.
This is odd, too:
[self.tableData removeAllObjects], [self.tableData release], tableData = nil;
First, don't bother removing the objects. When the array is deallocated, it'll release all objects. Secondly, using the setter to call release and then immediately do a direct assignment is inconsistent. Either do:
self.tableData = nil;
Or:
[tableData release], tableData = nil;
(Note that the use of the , in all of this is also purely for your benefit -- it has no impact on generated code.)
Also, use stringWithUTF8String: and not stringWithFormat:.
Not sure if it's the leak, but this looks like it's a problem:
self.tableData = [[NSMutableArray alloc] init];
You say that tableData is a property that's retained. Try:
self.tableData = [NSMutableArray arrayWithCapacity:10];
That way the property retains it and the array itself is autoreleased. Your release in dealloc will bring the retain count back down to zero.
The problem is that your property is set as retain, and you set it to an already retained object.
You should do it like this:
// viewDidLoad
NSMutableArray *array = [[NSMutableArray alloc] init];
self.tableData = array;
[array release]; // this is important
// dealloc
self.tableData = nil; // will automatically release the array
In your dealloc, you use properties which retain the tableData again. That is not really what you want, so do:
[tableData release];
or
[self->tableData release]; // not necessary, but some prefer it.
or
self.tableData = nil; // property will handle release
No need to clear the tableData, no need to set anything to nil (you are deallocating, so nothing will access it anymore).

Memory leak when refreshing table view with retain iOS

Most of the memoryleaks I solved myself, but this one is quite tough imo. The following happens. I need to load information from facebook in a table view, this table view has an refresh function. All the rows in this tablview are loaded from an array, this arrays consists of data objects as they need to be sorted. My code looks like this (I have cut out the irrelevant parts).
This parts runs through the results from facebook and places it in an array
- (void)request:(FBRequest*)request didLoad:(id)result
{
if ([result isKindOfClass:[NSDictionary class]]) {
//Setting single result into result dictionary
NSArray *resultArray = [result allObjects];
result = [resultArray objectAtIndex:0];
for(int i=0; i<13; i++){
//Set all retrieved data in containerArray
Post *newPost = [[[Post alloc] init] autorelease];
newPost.created_time = created_time1;
newPost.message = message1;
newPost.picture = picture1;
newPost.fbSource = fbSource1;
[containerArray insertObject:newPost atIndex:i];
//Reload the table in the tableView
[self.tableView reloadData];
}
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"created_time"
ascending:NO] autorelease ];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
}
}
So far this works and gives no memory leaks. But as soon as the refresh function gets called. This function will run again. And then creates the memory leak, I think probably due the [sortedArray retain] function. But without this function the array doesn't load and I get a EXC_BAD_ACCESS. If I release sortedArray, I also get the EXC_BAD_ACCESS since the sortedArray is gone and can't be called.
Someone knows how to fix this? Thnx!
Your diagnosis is right. If you assign a value to sortedArray a second time the way you are doing, the previous object is leaked.
The solution is calling release before doing the assignment:
[sortedArray release];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
A more elegant solution would be declaring sortedArray as a retain property:
#property (nonatomic, retain) NSArray* sortedArray;
so that you can replace the three lines above by:
self.sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
and this will handle both releasing and retaining properly.
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
This line runs for first time and this is OK. But run 2nd time and you are pointing to a new array, leaking the previous one. So there is two solution.
First, release it before this line like this:
[sortedArray release];
sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];
[sortedArray retain];
Or make sortedArray a retained property in your class.
#property (nonatomic, retain) NSArray *sortedArray;
self.sortedArray = [containerArray sortedArrayUsingDescriptors:[NSMutableArray arrayWithObject:sortDescriptor]];

How to persist data using archiving function on the iphone?

I am having trouble with persisting data.
Desired Behavior:
I have a text field defined in the view that captures user input. When the submit button is clicked (touch up inside), I expect to write that data to a mutable array. As of right now, I am going to archive that mutable array upon submit, but have also played with archiving once the view disappears/unloads. When a user navigates away from the view or exits the application and re-enters, I want to display the first element of that mutable array in the textfield.
Actual Behavior:
Textfield captures data and updates myMutableArray upon submitting. I have a few labels on the view plus NSLog to verify the count grows as I hit submit. I then archive this the first time, and I see that my file now exists in myDocuments plus when I revisit the view and check the count of dataArray, which is created if that file exists, the count of dataArray matches the number of elements I created in myMutableArray. If I start to enter in data again, it resets myMutableArray and dataArray all over again. That is one of a couple problems I think I have.
Questions:
If the file exists, then dataArray is created. Now I try to set the textfield to display the first element and the program bombs out! If dataArray exists, and I can see count is some positive value, I don't understand why I can't access that first element.
I feel like when I visit the view and go through this exercise, when I revisit the view I see I have dataArray present and with positive values, but when I start adding text again through the submit button, I reset everything! So I am persisting once maybe, but then wiping it clean. So much for real persistence. What am I doing wrong?
Eventually, I will be storing a record with a few elements. I will create multiple records over time for the user to review. Is archiving the best possible way? Should I start using NSCoding instead?
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize mutable array and set equal to myMutableArray property
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
// Debug Logging
// NSLog(#"Mutable Array Count is: %i", [myMutableArray count]);
NSFileManager *filemgr;
NSString *docsDir;
NSArray *dirPaths;
filemgr = [NSFileManager defaultManager];
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the data file
dataFilePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"data.archive"]];
// Check if the file already exists
if ([filemgr fileExistsAtPath: dataFilePath])
{
NSMutableArray *dataArray;
dataArray = [NSKeyedUnarchiver unarchiveObjectWithFile: dataFilePath];
myTextField.text = #"%#", [dataArray objectAtIndex:0]; // THIS BOMBS OUT MY PROGRAM FOR SURE IF INCLUDED!!!
//myUpdatedMutableArray = dataArray;
// Debug Logging
NSLog(#"Mutable Array (dataArray) Count is: %i", [dataArray count]);
//NSLog(#"The first value in the array is: %i", [dataArray objectAtIndex:0]);
}
[filemgr release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (IBAction) mySubmitButtonPressed:(id)sender {
// Debug Logging
NSLog(#"Submit Button was Pressed!");
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// Create string from textfield and addobject to myMutableArray; check to see that myMutableArray grows in count
NSString *myString = [[NSString alloc] initWithFormat:#"%#",myTextField.text];
[myMutableArray addObject:myString];
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// More Debug Logging just using on-screen labels
NSString *mySecondString = [[NSString alloc] initWithFormat:#"%i", [myMutableArray count]];
myFirstLabel.text = myString;
mySecondLabel.text = mySecondString;
// Archive myMutableArray
[NSKeyedArchiver archiveRootObject: myMutableArray toFile:dataFilePath];
//[contactArray release];
}
There are many issues with this code.
To begin, you leak memory all over the place. Specifically here:
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
Which should really be:
self.myMutableArray = [NSMutableArray array];
After you unarchive the data, if you want to keep it in myMutableArray, simply do this:
[myMutableArray appendArray: dataArray];
Or just:
self.myMutableArray = dataArray;
You should never release singleton instances like the NSFileManager, so completely get rid of this one:
[fileMgr release];
Your code crashes because this is not valid code:
myTextField.text = #"%#", [dataArray objectAtIndex:0];
(It actually is valid code, but it will do something very different than you think)
This is not Python, so you will have to do this:
myTextField.text = [NSString stringWithFormat: #"%#",
[dataArry objectAtIndex: 0]];
But that is really the same as:
myTextField.text = [dataArray objectAtIndex: 0];
Adding a string to your array in mySubmitButtonPressed can be done much simpler than you think. Just do this:
[myMutableArray addObject: myTextField.text];
You should really learn about autoreleased objects, memory management and what retaining properties actually do.

iPhone app memory leak

Any idea why this code gives me a memory leak? As you can see, I'm running out of ideas as to what I can do to stop it.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSArray *allSketches = [project.sketches allObjects];
NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:allSketches];
if(sketchesArray != nil) [sketchesArray release];
[self setSketchesArray:temp];
[allSketches release];
allSketches = nil;
[temp release];
temp = nil;
}
I also release sketchesArray inside viewDidDissapear. I'm not using viewDidLoad and dealloc to init/release these objects as what I am doing requires me to use viewWillAppear and viewDidDissapear.
Thanks
Fixed it by using this instead:
NSArray *allSketches = [project.sketches allObjects];
NSMutableArray *temp = [[NSMutableArray alloc] initWithArray:allSketches];
[self setSketchesArray:temp];
[temp release];
Though I remember doing that and it didn't work before... Strange... There appear to still be some memory leaks coming from CoreGraphics though. Is that normal?
Is this being released somewhere else when you are done with it?
[self setSketchesArray:temp];
Specifically, you are releasing sketchesArray in this function, but do you do that elsewhere when you are done with the view?
I can't see the leak, but you've got a couple of probable over-releases.
The release of a non-nil sketchesArray should be managed inside setSketchesArray. And it doesn't look like you have local ownership of allSketches either...

NSData release is not reclaiming memory

iPhoneOS 3.2
I use NSKeyedUnarchiver's unarchiveObjectWithFile: to load a custom object that contains a single large NSData and another much smaller object. The dealloc method in my custom object gets called, the NSData object is released, its retainCount == 1 just before. Physical memory does not decrement by any amount, let alone a fraction of the NSData size, and with repetition memory warnings are reliably generated: I have test until I actually received level 2 warnings. =(
NSString *archivePath = [[[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"] retain];
lingeringDataContainer = [[NSKeyedUnarchiver unarchiveObjectWithFile:archivePath] retain];
[archivePath release];
[lingeringDataContainer release];
and now the dealloc....
- (void) dealloc {
[releasingObject release];
[lingeringData release];
[super dealloc];
}
Before release:
(gdb) p (int) [(NSData *) lingeringData retainCount]
$1 = 1
After:
(gdb) p (int) [(NSData *) lingeringData retainCount]
Target does not respond to this message selector.
First, you're retaining and releasing objects which do not need to have that happen to them. Here's the cleaned up code:
NSString *archivePath = [[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"]; // Do not retain again.
lingeringDataContainer = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath]; // Do not retain again.
// Do not release, because archivePath is already autoreleaed: [archivePath release];
// Again, this is already autoreleased: [lingeringDataContainer release];
Or more simply:
NSString *archivePath = [[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"];
lingeringDataContainer = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
Second, where's the rest of the code? It's probably something else which is being retained or cached somewhere else.
When are you checking the memory usage? Is it just after the code snippet you posted? archivePath and lingeringDataContainer are both autoreleased. They will not be deallocated until (at least) the autorelease pool is drained (usually at the end of the current event). There may also be a lot of other internal stuff that has been autoreleased and won't go away until the pool is drained.
Try doing this:
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // <==
NSString *archivePath = [[[NSBundle mainBundle] pathForResource:#"lingering"]
ofType:#"data"] retain];
lingeringDataContainer = [[NSKeyedUnarchiver unarchiveObjectWithFile:archivePath] retain];
[pool release]; // <==
[archivePath release];
[lingeringDataContainer release];