I am trying to downloading more then 600 images in loop with a progress meter on the top of the screen to the user. I blocked my screen with a fade layer for showing activity and progress.
I am getting the memory warning message in between and app getting crashes.
My steps to reach the loop are :
- On app delegate, I check first core data table for all rows which is having “0” value in isImageAvailable bool field.
- If shows me some count (say 600), and I show and alert with YES and NO option.
- On YES : [self performSelector:@selector(myDownload:) withObject:nil afterDelay:0.2];
-
in myDownload
NSOperationQueue *queue = [NSOperationQueue new]; // Create our NSInvocationOperation to call loadDataWithOperation, passing in nil NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startUpdatingRecords:) object:nil]; // Add the operation to the queue [queue addOperation:operation]; [operation release]; [queue release]; -
in startUpdatingRecords :
-(void)startUpdatingRecords:(id)sender { [self performSelectorInBackground:@selector(updateProgressMeter:) withObject: [NSString stringWithFormat:@"%d",self.loopStartIndex]]; // Variable declarations CGSize newSizeLarge ; NSPredicate *predicate; NSMutableArray *MatchingID; Image_DB *data; // Cache Directory path NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSData *responseData; // = [[NSData alloc]init] ; NSURL *url = [[[NSURL alloc]init] autorelease]; NSMutableURLRequest *request = [[[NSMutableURLRequest alloc]init] autorelease]; UIImage *imgSelected_Large = [[[UIImage alloc]init] autorelease]; // Loop through all IDs for (int i = 0; i < [self.arrayOfID count]; i++) //for (int i = loopStart; i < loopEnd; i++) { if (self.abortDownload) { break; } NSString *documentsDirectory = [[[NSString alloc] initWithFormat:@"%@",[paths objectAtIndex:0]] autorelease]; documentsDirectory = [paths objectAtIndex:0]; documentsDirectory = [documentsDirectory stringByAppendingFormat:@"/ImageFolder"]; // Image folder path myClass *classObj = [self.arrayOfID objectAtIndex:i]; NSString *strURl = [[[NSString alloc] initWithFormat:@"%@%@", self.MyURL,recipeObj.recipeImageStr] autorelease]; //NSLog(@"URL = %@",strURl); url = [NSURL URLWithString:strURl]; request = [NSMutableURLRequest requestWithURL:url]; responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL]; // Get Image Data into NSData //imgSelected_Large = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:strURl]]]; NSLog(@"Download Count = %d",i+1); if (responseData != nil) { imgSelected_Large = [UIImage imageWithData:responseData]; // Resizining image newSizeLarge.width = 320; newSizeLarge.height = 180; imgSelected_Large = [self imageWithImage:imgSelected_Large scaledToSize:newSizeLarge]; // New sized image NSData *dataPhoto; // no need to release it because UIImageJPEGRepresentation gives autoreleased NSData obj. dataPhoto = UIImageJPEGRepresentation(imgSelected_Large, 0.6); // Set new image representation and its Compression Quality documentsDirectory = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"Image_%d", classObj.nodeID]]; [dataPhoto writeToFile:documentsDirectory atomically:YES]; //Write file to local folder at default path predicate = [NSPredicate predicateWithFormat: @"(image_ID = %d )",recipeObj.nodeID]; MatchingID = [CoreDataAPIMethods searchObjectsInContext:@"Image_DB" :predicate :@"image_ID" :YES :self.managedObjectContext]; // Setting flag variable for available image for (int j = 0; j< [MatchingID count]; j++) { //Assign the Authors Records in Class Object and save to Database data = (Image_DB*) [MatchingID objectAtIndex:j]; // data.image_large = dataPhoto; // Code for storing BLOB object to DB data.extra_1 = @"1"; //NSLog(@"Flag updated"); } } // Exit out code if ( i == [self.arrayOfID count] - 1 || i == [self.arrayOfID count]) // Its the last record to be stored { NSError *error; if (![self.managedObjectContext save:&error]) { // Handle the error... NSLog(@"Error in updating %@",error); } self.isUpdateImageCalled = NO; [self performSelectorOnMainThread:@selector(removeProgressMeter) withObject:nil waitUntilDone:NO]; } // Update UI screen while in downloading process [self performSelectorInBackground:@selector(updateProgressMeter:) withObject:[NSString stringWithFormat:@"%d",self.loopStartIndex+i+1]]; } }
If I didn’t release responseData then my app shows me memory warning and got crashed. If I released then, [NSConcreteMutableData release]: message sent to deallocated instance 0x1e931de0 error occures.
How to refine my code. Can any one suggest me on my code and rework and make a refined code.
Please please help me out.
Your
responseDatareturned bysendSynchronousRequestis autoreleased thus you shouldn’t release it yourself. For the first sight I don’t see a memory leak in your code. It is possible that your application actually uses too much memory, without leaking it. Try to place an autorelease pool inside your for cycle:If you wrap your code within an autorelease pool, all objects that are sent the
autoreleasemessage inside the wrap will be actually released when the pool itself is released: this way you purge the memory in every for cycle.See also Using Autorelease Pools in the doc, it specifically mentions that you should use them in the case “if you write a loop that creates many temporary objects”.