Instruments Leaks tells about a memory leak when initializing object - iphone

Can anyone please tell me why the following code leaks? Instruments tells me about 2 leaks. The 2 lines that obviously cause the leak are:
Person *pers = [[Person alloc] init];
and
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
The whole is listed below:
PersonViewController *personenDatenController = [[PersonViewController alloc]
initWithStyle:UITableViewStyleGrouped];
personenDatenController.view.backgroundColor = [UIColor clearColor];
Person *pers = [[Person alloc] init];
NSString *path = [[self class] pathForDocumentWithName:#"Person.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if (!fileExists) {
NSLog(#"file does not exist yet");
NSString *content = #"";
NSData *fileContents = [content dataUsingEncoding:NSUTF8StringEncoding];
[[NSFileManager defaultManager] createFileAtPath:path
contents:fileContents
attributes:nil];
}
NSMutableDictionary *dict = [[NSMutableDictionary alloc]
initWithContentsOfFile:path];
[pers setVorName:[dict valueForKey:#"vorName"]];
[pers setNachName:[dict valueForKey:#"nachName"]];
[pers setStrassenName:[dict valueForKey:#"strassenName"]];
[pers setHausNummer:[dict valueForKey:#"hausNummer"]];
[pers setPlz:[dict valueForKey:#"plz"]];
[pers setStadt:[dict valueForKey:#"stadt"]];
[pers setHandyNummer:(NSInteger*)[dict valueForKey:#"handyNummer"]];
[pers setEmail:[dict valueForKey:#"email"]];
[pers setSteuerSatz:[[dict valueForKey:#"steuerSatz"] floatValue]];
[dict release];
[personenDatenController setPerson:pers];
[navigationController pushViewController:personenDatenController animated:YES];
[personenDatenController release];
[pers release];
The variable "path" comes from the following static method:
+ (NSString *)pathForDocumentWithName:(NSString *)documentName
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *tempPath = [documentsDirectory stringByAppendingPathComponent:documentName];
return tempPath;
}
Thanks in advance for your help!
Kind regards
Phil

Assuming that setPerson calls retain on pers. Does your PersonViewController dealloc, call release on that person object? If so, put a breakpoint there (or NSLog) and find out the retainCount of the person. If it's not going to 0, where else might you have retained it?

Thank you guys for your responses.
PersonViewController does retain the person object but I put a release for the person object in dealloc. The retaincount is okay.
I moved the initialization of the Person object to the PersonViewController and now everything is fine. This seems quite strange to me.
Thank you
Regards

Related

Memory Leaks while using MFMailComposer

I am trying to send a CSV file through MfMail Composer.Everything works fine but there are lot leaks while using Instruments.I am not able trace out where I went wrong.Here is my code.
-(NSData *)getCSV
{
NSManagedObjectContext *moc = [(ETAppDelegate *)[[UIApplication sharedApplication] delegate]managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc]init]autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Expense" inManagedObjectContext:moc]];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
NSMutableArray *expensesList = [[[NSMutableArray alloc]init]autorelease];
for(int i = 0;i<[results count];i++){
NSString *category = [[results objectAtIndex:i]valueForKey:#"category"];
NSString *date = [[NSDateFormatter dateFormatterwithMediumStyle]stringFromDate:[[results objectAtIndex:i]valueForKey:#"date"]];
NSString *amount = [NSNumberFormatter localizedStringFromNumber:[[results objectAtIndex:i]valueForKey:#"amount"] numberStyle:NSNumberFormatterCurrencyStyle];
NSString *mailString = [NSString stringWithFormat:#"%#,%#,%#",category,date,amount ];
[expensesList addObject:mailString];
}
NSString *expensesString = [expensesList componentsJoinedByString:#"\n"];
NSData *expensesData = [expensesString dataUsingEncoding:NSUTF8StringEncoding];
return expensesData;
}
-(void)displayComposerSheet
{
NSData *csvFile = [self getCSV];
NSString *csvFileName = #"MyExpenses";
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc]init];
[mailController setSubject:#"List Of Expenses"];
[mailController setMessageBody:#"Expenses" isHTML:NO];
[mailController addAttachmentData:csvFile mimeType:#"text/csv" fileName:csvFileName];
[mailController setMailComposeDelegate:self];
[self presentModalViewController:mailController animated:YES];
[mailController release];
}
I can't say that I see anything in the code you've provided that should cause you to leak. Your leaks are likely taking place elsewhere.
What I do see, however, is the potential to create a lot of autoreleased objects in that loop in -getCSV, and depending on how many iterations you're performing, that could be almost as bad. The way you've written it, I see a date formatter and three strings headed for your main autorelease pool with every iteration. One suggestion is to create a date formatter outside your loop that you can reuse inside it. Another suggestion is to frame the guts of your loop with a local autorelease pool; that will prevent your main autorelease pool from becoming too large.
Apropos of nothing, you should also consider using fast enumeration.
Here's your loop with the suggestions applied:
NSDateFormatter *myDateFormatter = [NSDateFormatter dateFormatterWithMediumStyle];
NSMutableArray *expensesList = [[[NSMutableArray alloc] init] autorelease];
for (id obj in results)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *category = [obj valueForKey:#"category"];
NSString *date = [myDateFormatter stringFromDate:[obj valueForKey:#"date"]];
NSString *amount = [NSNumberFormatter localizedStringFromNumber:[obj valueForKey:#"amount"] numberStyle:NSNumberFormatterCurrencyStyle];
NSString *mailString = [NSString stringWithFormat:#"%#,%#,%#", category, date, amount];
[expensesList addObject:mailString];
[pool release];
}

Instance Variable not allocating in memory

I'm having an NSObject class where i have an init method defined something like below,
- (id)initWithPlistName:(NSString *)plistFileName{
if (self = [super init]) {
plistName = plistFileName;
plistContent = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:plistName ofType:#"plist"]]; // this plistContent array is not allocating in memory
}
return self;
}
I'm calling this method in my applications AppDelegate Class didFinishLaunchingWithOptions method, plistContent is my iVar of type NSArray but whenever control comes to plistContent alloc init line and while returning self, there is no memory allocated for my array.
What may be the problem happening here, Any help is appreciated in advance.
I suppose you have not changed the Datatype of your plist root key in your plist flie from dictionary to array
Check file exists:
NSString *path = [[NSBundle mainBundle] pathForResource:plistName ofType:#"plist"];
if(path)
{
plistContent = [[NSArray alloc] initWithContentsOfFile:path];
}
else
{
NSLog(#"File Not exists");
}
Try this
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"plistName" ofType:#"plist"];
NSDictionary *myDict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
NSArray *array = [NSArray arrayWithArray:[myDict objectForKey:#"Root"]];

NSNotification Called, but label will not update

I have the following code called with NSNotification center, i know its being called because the array is appearing in the NSLog, but my label chipCount is not updating with the new value. Is there perhaps a method I applied wrong when extracting the string from the array?
-(NSString *) dataFilePath {
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent:#"Chips.plist"];
}
-(void)readPlist {
[self dataFilePath];
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
chipArray = [[NSArray alloc] initWithContentsOfFile:filePath];
NSLog(#"%#\n", chipArray);
NSLog(#"%#\n", filePath);
NSString *chipcountString = [chipArray objectAtIndex:0];
chipsFloat = [chipcountString intValue];
chipCount.text = [NSString stringWithFormat:#"%i", chipsFloat];
//[arrayForPlist release];
}
}
I think this is a multithread problem. Update UI must in the main thread. Maybe readPlist is
executed in another thread.
Try this code below, maybe it can help you:
[self performSelectorOnMainThread:#selector(theProcess:) withObject:nil waitUntilDone:YES];
- (void) theProcess:(id)sender
{
....
chipCount.text = [NSString stringWithFormat:#"%i", chipsFloat];
}
Assusming that chipcount is a UILabel.. is it possible that you need to tell it to refresh the label?
[chipcount setNeedsDisplay];
Just an idea.

Help with Memory leak: init NSMutableArray from file

In some point in my app, I need to load a list from a file, so I implement this method to load the list:
-(void)loadList
{
NSString *filePath = [self dataFilePath]; //This is a method return the path of file
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSMutableArray *tempArray = [[NSMutableArray alloc]initWithContentsOfFile:filePath];
self.list = [[NSMutableArray alloc]initWithArray:tempArray];
[tempArray release];
}
}
The self.list is a (retain) property.
I think the leak is from [alloc] when I init the selfl.list. I used
self.list = [[[NSMutableArray alloc]initWithArray:tempArray]autorelease];
But the app crashes due to EXC_BAD_ACCESS. So I am confused here how to solve this.
Thanks for any suggestions.
Just assign,
self.list = tempArray;
As tempArray is already an array, you don't have to create another array from it. You ca directly assign to self.list.
There is no need to allocate another time for array .So just assign directly
-(void)loadList
{
NSString *filePath = [self dataFilePath]; //This is a method return the path of file
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSMutableArray *tempArray = [[NSMutableArray alloc]initWithContentsOfFile:filePath];
self.list = [tempArray copy];
[tempArray release];
}
}
Don't autorelease it. (I guess).
is your list property assign or retain?
if it is retain, then you should change this:
self.list = [[NSMutableArray alloc]initWithArray:tempArray];
to this:
self.list = [[NSMutableArray arrayWithArray:tempArray];

Cannot figure out why my app crashes when I use NSKeyedArchivers / NSKeyedUnarchivers

I am developing my first iphone 'Diary' app, which uses custom 'Entry' objects that hold an NSString title, NSString text and NSDate creationDate. When I try to archive an NSMutableArray of Entry objects, and later retrieve them the next time the view loads, the app crashes. I have gone through a bunch of sample codes and examples that use NSKeyedArchivers, but still couldn't figure out why that happens. I am guessing there is a problem with the initialization of the array that holds the entries but not sure...
Here is the code, maybe you could find something that I have persistently overseen..."
//--------- Entry.m---------------
- (id) initWithCoder:(NSCoder *)aDecoder{
if ((self = [super init])) {
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.title forKey:#"title"];
[aCoder encodeObject:self.text forKey:#"text"];
[aCoder encodeObject:self.created forKey:#"created"];
}
//-------------- Diary View Controller.m
- (NSString *)dataFilePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilename];
}
- (void) writeDataToArchive {
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:data];
[archiver encodeObject:self.entriesArray forKey:#"entriesArray"];
[archiver finishEncoding];
BOOL result = [data writeToFile:[self dataFilePath] atomically:YES];
[archiver release];
[data release];
}
- (void)addItem:sender {
int count = [entriesArray count] +1;
NSString *newEntryTitle = [NSString stringWithFormat:#"Entry %d", count];
Entry *anEntry = [[Entry alloc] initWithTitle:newEntryTitle text:#"-"
created:[NSDate date]];
[entriesArray addObject:anEntry];
[self.tableView reloadData];
[anEntry release];
[self writeDataToArchive];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *data = [[NSMutableData alloc]
initWithContentsOfFile:[self dataFilePath]];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]
initForReadingWithData:data];
NSMutableArray *array = [unarchiver decodeObjectForKey:#"entriesArray"];
entriesArray = [array mutableCopy];
[array release];
[unarchiver finishDecoding];
[unarchiver release];
[data release];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
// ... some other stuff
NSUInteger row = indexPath.row;
Entry *entry = [entriesArray objectAtIndex:row];
cell.textLabel.text = entry.title;
return cell;
}
Thanks a lot.
When you read an array back out with NSKeyedUnarchivers you always get an unmutable copy back. You would need to declare *array as NSArray or just get rid of array all together.
entriesArray = [[unarchiver decodeObjectForKey:#"entriesArray"] mutableCopy];
And #JeremyP points out another issue. Since you didn't alloc or retain *array you should not release it.
You should not release array in viewDidLoad because you do not own it.
Please review the Cocoa memory management Rules because there are a couple of other memory management issues in your code. In particular,
self.title = [[aDecoder decodeObjectForKey:#"title"] retain];
self.text = [[aDecoder decodeObjectForKey:#"text"] retain];
self.created = [[aDecoder decodeObjectForKey:#"created"] retain];
in your initWithCoder: method all leak on the assumption the properties are retain or copy.