Iphone Allocation with NSData - iphone

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.

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];
}

Instruments Leaks tells about a memory leak when initializing object

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

if my retainCount is 4 is that bad for iphone

Updates code
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
mytimer3=[NSTimer scheduledTimerWithTimeInterval:.03 target:self selector:#selector(show) userInfo:nil repeats:NO];
NSLog(#" search is down");
//////////// rnd to hold keyboard
//ovController.view.backgroundColor = [UIColor grayColor];
self.tableView.scrollEnabled = NO;
UserText=[self.searchDisplayController.searchBar text];
myInt= UserText.length;
//int myInt= [save length];
// NSString *myStringPrt1 = [[NSString alloc] init];
// NSString *myStringPrt2 = [[NSString alloc] init];
if(UserText.length>3)
{
//[ovController.view removeFromSuperview];
//[tableView reloadData];
url=nil;
// myStringPrt1=#"http://find.php?keyword=";
NSString * myStringPrt2=UserText;
// myStringPrt1=[myStringPrt1 stringByAppendingString:myStringPrt2];
// myStringPrt1 = [myStringPrt1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// loadingView.hidden=NO;
NSString *outputString = [[NSString stringWithString:#"http://find.php?keyword="] stringByAppendingString: UserText];
outputString = [outputString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(#"My string is now = %#", outputString);
url= [NSURL URLWithString:outputString];
NSXMLParser *xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease];
//Initialize the delegate.
XMLParser *parser = [[[XMLParser alloc] initXMLParser]autorelease];
//Set delegate
[xmlParser setDelegate:parser];
//Start parsing the XML file.
BOOL success = [xmlParser parse];
if(success)
{
[super viewDidLoad];
[self searchTableView];
mytimer4=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(wipe) userInfo:nil repeats:NO];
}
}
}
Right, you have memory leaks all over the place:
NSString *myStringPrt1 = [[NSString alloc] init];
NSString *myStringPrt2 = [[NSString alloc] init];
url=nil;
myStringPrt1=#"http://wap?keyword="; //MEMORY LEAK, this will assign a new string to myStringPrt1, without releasing the first one
myStringPrt2=UserText; //MEMORY LEAK, this will assign a new string to myStringPrt2, without releasing the first one
myStringPrt1=[myStringPrt1 stringByAppendingString:myStringPrt2]; //MEMORY LEAK, this will assing an autoreleased string the myStringPrt1, without releasing the old one first.
myStringPrt1 = [myStringPrt1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //MEMORY LEAK, this will assing an autoreleased string the myStringPrt1, without releasing the old one first.
I suggest you first read the memory management guidelines as indicated by Kubi. Or, if you are feeling lazy, use this:
NSString *outputString = [[NSString stringWithString:#"http://wap?keyword="] stringByAppendingString: UserText];
outputString = [outputString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Or this:
NSString *outputString = [NSString stringWithFormat:#"http://wap?keyword=%#",UserText];
outputString = [outputString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
Don't pay any attention to your retain count. That number will fluctuate for reasons entirely out of your control and trying to determine why the number is what it is will drive you crazy (and not do you any good).
Follow proper Obj-C memory management guidelines, use the leaks detector in Instruments, and run the static analyzer when you build. If you do all that, you'll be fine.
No depending on your requirement and object allocation, your retain count will definitely increase. But the main thing you need to keep in mind that release all the allocated object at proper place and proper time.

UIImagePickerController Save to Disk then Load to UIImageView

I have a UIImagePickerController that saves the image to disk as a png. When I try to load the PNG and set a UIImageView's imageView.image to the file, it is not displaying.
Here is my code:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *imageData = UIImagePNGRepresentation(image);
// Create a file name for the image
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
NSString *imageName = [NSString stringWithFormat:#"photo-%#.png",
[dateFormatter stringFromDate:[NSDate date]]];
[dateFormatter release];
// Find the path to the documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// Now we get the full path to the file
NSString *fullPathToFile = [documentsDirectory stringByAppendingPathComponent:imageName];
// Write out the data.
[imageData writeToFile:fullPathToFile atomically:NO];
// Set the managedObject's imageLocation attribute and save the managed object context
[self.managedObject setValue:fullPathToFile forKey:#"imageLocation"];
NSError *error = nil;
[[self.managedObject managedObjectContext] save:&error];
[self dismissModalViewControllerAnimated:YES];
}
Then here is how I try to load it:
self.imageView.backgroundColor = [UIColor lightGrayColor];
self.imageView.frame = CGRectMake(10, 10, 72, 72);
if ([self.managedObject valueForKey:#"imageLocation"] != nil) {
NSLog(#"Trying to load the imageView with: %#", [self.managedObject valueForKey:#"imageLocation"]);
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self.managedObject valueForKey:#"imageLocation"]];
self.imageView.image = image;
} else {
self.imageView.image = [UIImage imageNamed:#"no_picture_taken.png"];
}
I get the message that it's trying to load the imageView in the debugger, but the image is never displayed in the imageView. Can anyone tell me what I've got wrong here?
Thanks a bunch.
You're writing out an NSData object, not an image. You need to read the NSData object back in and convert to UIImage.
Assuming everything else is correct, try this:
NSData *data = [[NSData alloc] initWithContentsOfFile:[self.managedObject valueForKey:#"imageLocation"]];
UIImage *image = [[UIImage alloc] initWithData:data];
Using NSDateFormatterShortStyle will cause the date to be represented as (for example) 12/11/12, which will mess up your file name. It will also use a colon in the short time representation, which is illegal.
I would recommend using a more custom date format, such as:
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:locale];
[dateFormatter setDateFormat:#"yyyyMMdd-HHmmss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
I used this code on one of my test projects and simulated the bug. Jordan was right about converting it to NSData first before changing it again to UIImage but the real problem here is on your file naming method.
I noticed that you have used the current date and time as the file name of your image. This includes characters such as ":" and "/" which is not allowed to be used on file names. http://www.portfoliofaq.com/pfaq/FAQ00352.htm
As a solution, I made the date format as what is written below:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyyMddHHmm"];
NSString *imageName = [NSString stringWithFormat:#"photo-%#.png",
[dateFormatter stringFromDate:[NSDate date]]];
Hope this helps others as well. :)

iPhone Memory Leak JSON Library

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?