1

I have a method that saves a bunch of UIImages as JPGs and my app is crashing, I believe due to memory not being released. I'm using UIImageJPEGRepresentation to save the images, and I'm wrapping it in an autorelease pool but it doesn't seem to be working.

       for (Images *anImage in images) {

            NSAutoreleasePool* p = [[NSAutoreleasePool alloc] init];

            NSString *fileName = [NSString stringWithFormat:@"%@-%i-%i-%i.jpg", appDelegate.currentUserName, appDelegate.reportId, aDefect.defectId, i];

            [UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO];

            [p drain];

            i++;

        }

When I run the code above, the analyser shows me that more and more memory is being used, and it eventually crashes. If I remove the line - [UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO]; it works fine.

FYI the loop iterates through an array of NSManagedObjects.

Any help would be appreciated! Thanks

Here is the new code as per suggestion -

- (void) convertImages {

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Report" inManagedObjectContext:context];
NSPredicate *predicate = [NSPredicate
                          predicateWithFormat:@"status != %@", @"Leads"];

[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setIncludesPropertyValues:NO];

NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];

for (Report *aReport in fetchedObjects) {

    appDelegate.reportId = [aReport.reportId intValue];

    NSArray *defects = [self getAllItemDefects];

    for (ItemDefect *anItemDefect in defects) {

        NSArray *defectImages = [self getImages:[anItemDefect.itemDefectId intValue] isMainImage:NO];

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
        dispatch_async(queue, ^(void) {

            int i = 0;

            for (Images *anImage in defectImages) {

                NSString *fileName = [NSString stringWithFormat:@"%@-%i-%i-%i.jpg", appDelegate.currentUserName, [aReport.reportId intValue], [anItemDefect.itemDefectId intValue], i];

                NSString *localImagePath = [documentsDirectory stringByAppendingPathComponent:fileName];

                [UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO];

                i++;

            }

            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"FINISH: do eventual operations");
            });

        });

    }

}

}

That gives me this error -

enter image description here

If I load the defectImages array within the dispatch block, it just does nothing. Thanks for your help.

Edit - As CoreData is not thread safe, I've declared new FetchRequests and ObjectContexts to fix that problem. I'm no longer getting the bad access error, but I'm back to running out of memory. I've simplified the code for you, but its producing a memory leak in this state -

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
        dispatch_async(queue, ^(void) {

            NSFetchRequest *backgroundFetchRequest = [[NSFetchRequest alloc] init];

            NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
            [backgroundContext setPersistentStoreCoordinator:persistentStoreCoordinator];
            NSEntityDescription *entity = [NSEntityDescription entityForName:@"Images" inManagedObjectContext:backgroundContext];
            NSPredicate *predicate = [NSPredicate
                                      predicateWithFormat:@"itemDefectId=%i AND reportId=%i AND isMainImage=%i", itemDefectId, appDelegate.reportId, NO];

            [backgroundFetchRequest setEntity:entity];
            [backgroundFetchRequest setIncludesPropertyValues:NO];
            [backgroundFetchRequest setPredicate:predicate];

            NSArray *defectImages = [backgroundContext executeFetchRequest:backgroundFetchRequest error:&error];

            NSMutableArray *images = [[NSMutableArray alloc] init];

            for (Images *anImage in defectImages) {
                [images addObject:anImage.image];
            }

            [images release];

            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"FINISH: do eventual operations");
            });
        });
Gary Thackrah
  • 98
  • 2
  • 7

1 Answers1

0

This isn't the good solution, use GCD:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

for (int i=0; i<[images count]; i++) {

    dispatch_async(queue, ^(void) {

        Images *anImage = (Images *)[images objectAtIndex:i];
        NSString *fileName = [NSString stringWithFormat:@"%@-%i-%i-%i.jpg", appDelegate.currentUserName, appDelegate.reportId, aDefect.defectId, i];

        [UIImageJPEGRepresentation(anImage.image, 1) writeToFile:localImagePath atomically:NO];

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"FINISH to write image %d", i);
        });

    });
}

Moreover, in your code, filename is ever the same, it doesn't change and isn't used. Your code is partial?

However, use dispatcher for async process.

Matteo Gobbi
  • 17,697
  • 3
  • 27
  • 41
  • This gives me a bad access error. Probably because the "images" array is loaded prior to this block of code. Apologies for the incomplete code, I removed the lines that aren't relevant. Do you have any more clues? I've made various changes based on your suggestion, but all give me errors. – Gary Thackrah Nov 19 '13 at 07:46
  • Added to original message – Gary Thackrah Nov 20 '13 at 00:58
  • Well, you have an EXEC_BAD_ACCESS, that can depend from the array images, because otherwise it should happen before. So, first one, let me know the state of the objets in the error's line, when there is the error. – Matteo Gobbi Nov 20 '13 at 10:03
  • I've added to original message again – Gary Thackrah Nov 21 '13 at 00:19
  • 1
    Its working! Thank you. FYI I had to add an auto release pool in the loop, and load the array inside the dispatch block. Thanks again for your help :) – Gary Thackrah Nov 22 '13 at 03:36
  • It's ok ;) So, please can you set as correct (with the green V) my answer? Thank you! – Matteo Gobbi Nov 22 '13 at 16:32