user3784214
user3784214

Reputation: 105

Objective-C: Uploading too many images memory pressure causing app to quit

I am using QBImagePicker to allow multiple image upload. It works fine for up to 25 images being downloaded, but more than that, and the app will quit do to memory pressure while uploading. I would like to allow infinite image upload, and am uncertain how to do so where memory would not be an issue (i.e. perhaps clearing memory after each save). Here is my method to save images (which is called from a loop within the main QBImagePickerController method to save all the selected images):

- (void) saveTheImage:(UIImage *)image fileName:(NSString *)name width:(CGFloat) width height:(CGFloat) height quality:(CGFloat) quality extension:(int)fileNumberExtension
{
UIImage *resizedImage = [self resizeImage:image width:width height:height]; //this is a simple method I have to resize the image sent from the picker
NSData *data = UIImageJPEGRepresentation(resizedImage, quality); //save as a jpeg
NSString *fileName = [NSString stringWithFormat:@"%@%d", name, fileNumberExtension]; //set the filename
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; //will be saved in documents
NSString *tempPath = [documentsDirectory stringByAppendingPathComponent:fileName]; //with the filename given

//create a block operation to save
NSBlockOperation* saveOp = [NSBlockOperation blockOperationWithBlock: ^{
    [data writeToFile:tempPath atomically:YES];
}];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:saveOp];
}

Thanks in advance!

EDIT

My method to resize the image:

- (UIImage *) resizeImage:(UIImage *)image width:(CGFloat) width height:(CGFloat) height
{
UIImage *resizedImage;
CGSize size = CGSizeMake(width, height);
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
[image drawInRect:CGRectMake(0, 0, width, height)];
resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return resizedImage;
}

EDIT 2

Additional methods:

- (void) imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets
{
for (int i=0;i<assets.count;i++)
{
    ALAssetRepresentation *rep = [[assets objectAtIndex:i] defaultRepresentation];
    CGImageRef iref = [rep fullResolutionImage];
    UIImage *pickedImage = [UIImage imageWithCGImage:iref scale:[rep scale] orientation:(UIImageOrientation)[rep orientation]];
    int fileNumberExtension = [self getHighestImageNumber] + 1; //new images all have a higher file name
    //set the ratio (width of image is 294)
    CGFloat ratio = pickedImage.size.width / 294;
    CGFloat newHeight = pickedImage.size.height / ratio;

    if (newHeight < 430) //image is too wide
    {
        [self saveTheImage:pickedImage fileName:@"img" width:294 height:newHeight quality:0.8f extension:fileNumberExtension];
    }
    else //if the image is too narrow
    {
        //set the ratio (height of image is 430)
        CGFloat ratio = pickedImage.size.height / 430;
        CGFloat newWidth = pickedImage.size.width / ratio;

        [self saveTheImage:pickedImage fileName:@"img" width:newWidth height:430 quality:0.8f extension:fileNumberExtension];
    }

    [self saveTheImage:pickedImage fileName:@"thm" width:78 height:78 quality:0.0f extension:fileNumberExtension]; //save the thumbnail
}

[self dismissImagePickerController];
}

- (void)dismissImagePickerController
{
[self dismissViewControllerAnimated:YES completion:nil];
}

- (void) addImageClicked
{
QBImagePickerController *imagePickerController = [[QBImagePickerController alloc] init];
imagePickerController.delegate = self;
imagePickerController.allowsMultipleSelection = YES;
imagePickerController.maximumNumberOfSelection = 20; //allow up to 20 photos at once
imagePickerController.filterType = QBImagePickerControllerFilterTypePhotos;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:imagePickerController];
[self presentViewController:navigationController animated:YES completion:nil];
}

Upvotes: 1

Views: 661

Answers (2)

user3784214
user3784214

Reputation: 105

Solved this issue by adding by using @autoreleasepool around my for loop in this method:

- (void) imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets

This thread was very useful.

Upvotes: 1

Jeffrey Sun
Jeffrey Sun

Reputation: 8049

You have a memory leak. Leaks usually don't happen because ARC takes care of it for you. (every time you finish using an image, it gets cleared from memory). However, NOT ALL objects are governed by ARC. There are some object types (like CGColorSpaceRef, etc.) that need to be freed manually.

You can check this by running Static Analysis in Xcode. In the top menu bar, select Product -> Analyze. If there are places where you need to free your objects, it will tell you.

To free an object, do:

CGColorSpaceRelease(ref); //where ref is a CGColorSpaceRef.
CGImageRelease(iref); //where iref is a CGImageRef.

or the corresponding method that pertains to your object.

Upvotes: 0

Related Questions