I'm getting an instruments Memory Leak with this iPhone 3.0 SDK code.
I'm using the JSON from http://code.google.com/p/json-framework/
Here is my code:
// .h
#property (nontatomic,retain) NSMutableArray *tweets;
// .m
import" JSON.h"
#synthesize tweets;
...
tweets = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:#"http://www.someurl.com"];
NSString *jsonString = [NSString stringWithContentsOfURL:url];
NSArray *results = [jsonString JSONValue];
NSArray *data = [results valueForKey:#"stories"];
for(NSDictionary *tweet in data) {
TweetmemeData *tweetmeme = [[TweetmemeData alloc] initWithTweet:tweet];
[tweets addObject:tweetmeme];
[self debugDump:tweetmeme];
[tweetmeme release];
}
[results release];
return tweets;
If possible, please explain more about this form of memory management. I'm very familiar with retain/release but obviously am having trouble implementing it :)
Thanks!
It's worth noting that many leaks that will come up in the simulator don't happen at all on the hardware. Are you using the simulator or testing it on the phone?
Related
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];
}
I have problem with the following code leaking memory...
#property (nonatomic, retain) NSMutableArray *childrensArray;
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Connection finished loading.");
// Dismiss the network indicator when connection finished loading
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// Parse the responseData of json objects retrieved from the service
SBJSON *parser = [[SBJSON alloc] init];
NSString *jsonString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSDictionary *jsonData = [parser objectWithString:jsonString error:nil];
childrensArray = [jsonData objectForKey:#"Children"];
// Callback to AttendanceReportViewController that the responseData finished loading
[attendanceReportViewController loadChildren];
[connection release];
[responseData release];
[jsonString release];
[parser release];
}
In the viewController the following also leaks memory...
#property (nonatomic, retain) NSMutableArray *childrensArray;
- (void)loadChildren {
// Retrieve a array with dictionaries of children from ServiceGetChildren
self.childrensArray = [[serviceGetChildren.childrensArray copy] autorelease];
int total = [childrensArray count];
totalLabel.text = [NSString stringWithFormat:#"%d", total];
[theTableView reloadData];
}
You only release childrensArray when the instance is deallocated. You should also release the instance variable before setting it:
- (void)loadChildren {
// Retrieve a array with dictionaries of children from ServiceGetChildren
[childrensArray release];
childrensArray = [serviceGetChildren.childrensArray copy];
}
A better way would be to actually use your property:
- (void)loadChildren {
// Retrieve a array with dictionaries of children from ServiceGetChildren
self.childrensArray = [[serviceGetChildren.childrensArray copy] autorelease];
}
(Note the autorelease)
This has the benefit of triggering KVO-Notifications should you ever use them.
I have a method that downloads many images from the internet, then saves them to the device. Here is the code:
-(IBAction) updateImages {
for (x=0; x<[imagesToUpdate count]; x=x+1) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSMutableDictionary *tempDic = [dic objectForKey:[imagesToUpdate objectAtIndex:x]];
BOOL newDictionary = [self downloadChartForDictionary:tempDic];
[pool drain];
}
}
-(BOOL) downloadChartForDictionary:(NSMutableDictionary *)dictionary {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSMutableDictionary *updatedDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionary];
NSString *downloadString = [NSString stringWithFormat:#"http://www.photo.com/%#_0001.jpg",[updatedDictionary objectForKey:#"PDFName"]];
[updatedDictionary setObject:serverAIRAC forKey:#"airac"];
NSDate *date = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
formatter.dateFormat = #"MM-dd-y";
NSString *dateString = [formatter stringFromDate:date];
[updatedDictionary setObject:dateString forKey:#"date"];
[formatter release];
NSURL *url = [NSURL URLWithString:downloadString];
NSData *data = [NSData dataWithContentsOfURL:url];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *savePath = [NSString stringWithFormat:#"%#/%#^%#_%#.jpg",docsPath,serverAIRAC,[updatedDictionary objectForKey:#"airport"],[updatedDictionary objectForKey:#"PDFName"]];
BOOL downloaded = [data writeToFile:savePath atomically:YES];
[pool drain];
return YES;
}
My problem is that when this runs, the NSData object, data, is allocated but never released, even when the updateImages method is finished. In instruments, each image that is downloaded remains allocated. The total allocations continues to rise as each image is downloaded and eventually there is a out of memory crash. Instruments does not report a leak associated with this object however.
I have tried: [[NSData alloc]initWithContentsOfURL:url] and then releasing it, but that doesn't help.
I have tried putting the autorelease pool only in the updateImages method and only in the downloadChartForDictionary method and neither helps.
I've been stuck on this all day so any help is greatly appreciated
Did you try [[[NSData alloc]initWithContentsOfURL:url] autorelease]? That will put the NSData object into the current autorelease pool. A straightforward alloc/init creation will still require your explicit management.
Try using dataWithContentsOfURL:options:error: with different reading options. I would start with NSDataReadingUncached, because it sounds like your data is being cached when you don't want it to be.
this was actually a non-issue... For some reason instruments was reporting the allocations, but when I actually ran the app, it never crashed.
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
I'm making an app where I need to save the text in multiple views in the app when the app quits. I also need to be able to remove all of the data from just one of those views and when the app quits, it's possible not all of those views will have been created yet.
After reading this post I thought perhaps it would be good to use a singleton that manages my app data which loads in the data when it is first requested and saved it when the app quits. Then in each view where I need to save data I can just set it on the singleton.
I gave it a go but have run into some issues. At first I didn't synthesize the properties (as in the post I was using as a guide) but the compiler told me I needed to make getters and setters, so I did. Now when my applicationWIllTerminate: gets call the app crashes and the console says "Program received signal: “EXC_BAD_ACCESS”. kill quit".
Is anyone able to tell me what I'm doing wrong, or suggest a better approach to saving the data?
//SavedData.h
#import <Foundation/Foundation.h>
#define kFileName #"appData.plist"
#interface SavedData : NSObject {
NSString *information;
NSString *name;
NSString *email;
NSString *phone;
NSString *mobile;
}
#property(assign) NSString *information;
#property(assign) NSString *name;
#property(assign) NSString *email;
#property(assign) NSString *phone;
#property(assign) NSString *mobile;
+ (SavedData *)singleton;
+ (NSString *)dataFilePath;
+ (void)applicationWillTerminate:(NSNotification *)notification;
#end
//SavedData.m
#import "SavedData.h"
#implementation SavedData
#synthesize information;
#synthesize name;
#synthesize email;
#synthesize phone;
#synthesize mobile;
static SavedData * SavedData_Singleton = nil;
+ (SavedData *)singleton{
if (nil == SavedData_Singleton){
SavedData_Singleton = [[SavedData_Singleton alloc] init];
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]){
NSMutableArray * array = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
information = [array objectAtIndex:0];
name = [array objectAtIndex:1];
email = [array objectAtIndex:2];
phone = [array objectAtIndex:3];
mobile = [array objectAtIndex:4];
[array release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
}
return SavedData_Singleton;
}
+ (NSString *)dataFilePath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *DocumentsDirectory = [paths objectAtIndex:0];
return [DocumentsDirectory stringByAppendingPathComponent:kFileName];
}
+ (void)applicationWillTerminate:(NSNotification *)notification{
NSLog(#"Application will terminate received");
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:information];
[array addObject:name];
[array addObject:email];
[array addObject:phone];
[array addObject:mobile];
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
}
#end
Then when I want to use it I do
myLabel.text = [SavedData singleton].information;
And when I change the field
[SavedData singleton].information = #"my string";
Any help will be very much appreciated!
You might want to change your properties to be (retain) instead of assign.
You might want to get the singleton header file I use. It is quite nice for me.
What is likely happening is that you load up your array from a file, and assign it to the property. But it is being autoreleased, and thus it doesn't exist anymore. So when you try to access the memory later, it crashes.
Further reading on memory management: http://www.cocoadev.com/index.pl?MemoryManagement