0
votes

My app is crashing due to heavy use of memory while I am doing image processing.

Basically my whole app depends on following method in which logic for combining images are used another method i used for loading images from bundle.

+ (UIImage*)imageByCombiningImages:(UIImage*)firstImage withImage:(UIImage*)secondImage
{
    @autoreleasepool
    {
        UIImage *image = nil;

        CGSize newImageSize = CGSizeMake(MAX(firstImage.size.width, secondImage.size.width), MAX(firstImage.size.height, secondImage.size.height));
        if (UIGraphicsBeginImageContextWithOptions != NULL)
        {
            UIGraphicsBeginImageContextWithOptions(newImageSize, NO, [[UIScreen mainScreen] scale]);
        }
        else
        {
            UIGraphicsBeginImageContext(newImageSize);
        }

        [firstImage drawAtPoint:CGPointMake(roundf((newImageSize.width-firstImage.size.width)/2), roundf((newImageSize.height-firstImage.size.height)/2))];

        firstImage =nil;
        [secondImage drawAtPoint:CGPointMake(roundf((newImageSize.width-secondImage.size.width)/2), roundf((newImageSize.height-secondImage.size.height)/2))];


        secondImage=nil;

        image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
}

- (UIImage*) getImage:(NSString*)imagename
{
    NSString *fileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:imagename];
    UIImage *image = [UIImage imageWithContentsOfFile:fileName];
    return image;
}

the above methods are called from following method numerous times

- (void) applyMaterialWithValue:(NSDictionary*)dict
{

    @synchronized(self)
    {

        NSString *materialName = [dict valueForKey:@"material"];
        NSString *optionName = [dict valueForKey:@"option"];
        //NSString *preset_Name = [dict valueForKey:@"preset"];
        NSString *name = [dict valueForKey:@"name"];
        if([[GlobalIntVariable getCMFScheme] isEqualToString:@"preset1"])

        {
            if(!isMetalPressed)
            {

                [GlobalIntVariable setCurrentMaterialName:@"Preset 1 Metal Plating"];
            }
            if(!isLeatherPressed)
            {
                [GlobalIntVariable setCurrentPresetName:@"preset1"];

            }

        }
        if([[GlobalIntVariable getCMFScheme] isEqualToString:@"preset2"])

        {

            if(!isMetalPressed)
            {

                [GlobalIntVariable setCurrentMaterialName:@"Preset 2 Metal Plating"];
            }
            if(!isLeatherPressed)
            {
                [GlobalIntVariable setCurrentPresetName:@"preset2"];

            }

        }

        if ([materialName isEqualToString:AREAD4METALCOMPONENET ]|| [materialName isEqualToString:AREA8METALCOMPONENET])
        {
            [GlobalIntVariable setCurrentMaterialName:name];
        }


        if([optionName isEqualToString:OPTION0])
        {
            @autoreleasepool
            {

                UIImage *presetImg1L1Standard = [APP_DEL getImage:[dict valueForKey:@"image1L1Standard"]];
                imgView1Layout1Standard.image = [ImageOperations imageByCombiningImages:imgView1Layout1Standard.image withImage:presetImg1L1Standard];
                imgView1Layout1StandardPSUOn.image = [ImageOperations imageByCombiningImages:imgView1Layout1StandardPSUOn.image withImage:presetImg1L1Standard];
                 presetImg1L1Standard=nil;



                UIImage *presetImg1L1Premium = [APP_DEL getImage:[dict valueForKey:@"image1L1Premium"]];
                imgView1Layout1Premium.image = [ImageOperations imageByCombiningImages:imgView1Layout1Premium.image withImage:presetImg1L1Premium];
                imgView1Layout1PremiumPSUOn.image = [ImageOperations imageByCombiningImages:imgView1Layout1PremiumPSUOn.image withImage:presetImg1L1Premium];
                presetImg1L1Premium=nil;

                UIImage *presetImg1L2Standard = [APP_DEL getImage:[dict valueForKey:@"image1L2Standard"]];
                imgView1Layout2Standard.image = [ImageOperations imageByCombiningImages:imgView1Layout2Standard.image withImage:presetImg1L2Standard];
                imgView1Layout2StandardPSUOn.image = [ImageOperations imageByCombiningImages:imgView1Layout2StandardPSUOn.image withImage:presetImg1L2Standard];
                presetImg1L2Standard=nil;

                UIImage *presetImg1L2Premium = [APP_DEL getImage:[dict valueForKey:@"image1L2Premium"]];
                imgView1Layout2Premium.image = [ImageOperations imageByCombiningImages:imgView1Layout2Premium.image withImage:presetImg1L2Premium];
                imgView1Layout2PremiumPSUOn.image = [ImageOperations imageByCombiningImages:imgView1Layout2PremiumPSUOn.image withImage:presetImg1L2Premium];
                presetImg1L2Premium=nil;

            }

            @autoreleasepool
            {

                UIImage *presetImg2L1Standard = [APP_DEL getImage:[dict valueForKey:@"image2L1Standard"]];
                imgView2Layout1Standard.image = [ImageOperations imageByCombiningImages:imgView2Layout1Standard.image withImage:presetImg2L1Standard];
                imgView2Layout1StandardPSUOn.image = [ImageOperations imageByCombiningImages:imgView2Layout1StandardPSUOn.image withImage:presetImg2L1Standard];
                presetImg2L1Standard=nil;

                UIImage *presetImg2L1Premium = [APP_DEL getImage:[dict valueForKey:@"image2L1Premium"]];
                imgView2Layout1Premium.image = [ImageOperations imageByCombiningImages:imgView2Layout1Premium.image withImage:presetImg2L1Premium];
                imgView2Layout1PremiumPSUOn.image = [ImageOperations imageByCombiningImages:imgView2Layout1PremiumPSUOn.image withImage:presetImg2L1Premium];
                presetImg2L1Premium=nil;

                UIImage *presetImg2L2Standard = [APP_DEL getImage:[dict valueForKey:@"image2L2Standard"]];
                imgView2Layout2Standard.image = [ImageOperations imageByCombiningImages:imgView2Layout2Standard.image withImage:presetImg2L2Standard];
                imgView2Layout2StandardPSUOn.image = [ImageOperations imageByCombiningImages:imgView2Layout2StandardPSUOn.image withImage:presetImg2L2Standard];
                presetImg2L2Standard=nil;

                UIImage *presetImg2L2Premium = [APP_DEL getImage:[dict valueForKey:@"image2L2Premium"]];
                imgView2Layout2Premium.image = [ImageOperations imageByCombiningImages:imgView2Layout2Premium.image withImage:presetImg2L2Premium];
                imgView2Layout2PremiumPSUOn.image = [ImageOperations imageByCombiningImages:imgView2Layout2PremiumPSUOn.image withImage:presetImg2L2Premium];
                presetImg2L2Premium=nil;


            }

            @autoreleasepool
            {

                UIImage *presetImg3L1Standard = [APP_DEL getImage:[dict valueForKey:@"image3L1Standard"]];
                imgView3Layout1Standard.image = [ImageOperations imageByCombiningImages:imgView3Layout1Standard.image withImage:presetImg3L1Standard];
                imgView3Layout1StandardPSUOn.image = [ImageOperations imageByCombiningImages:imgView3Layout1StandardPSUOn.image withImage:presetImg3L1Standard];
                presetImg3L1Standard=nil;

                UIImage *presetImg3L1Premium = [APP_DEL getImage:[dict valueForKey:@"image3L1Premium"]];
                imgView3Layout1Premium.image = [ImageOperations imageByCombiningImages:imgView3Layout1Premium.image withImage:presetImg3L1Premium];
                imgView3Layout1PremiumPSUOn.image = [ImageOperations imageByCombiningImages:imgView3Layout1PremiumPSUOn.image withImage:presetImg3L1Premium];
                presetImg3L1Premium=nil;

                UIImage *presetImg3L2Standard = [APP_DEL getImage:[dict valueForKey:@"image3L2Standard"]];
                imgView3Layout2Standard.image = [ImageOperations imageByCombiningImages:imgView3Layout2Standard.image withImage:presetImg3L2Standard];
                imgView3Layout2StandardPSUOn.image = [ImageOperations imageByCombiningImages:imgView3Layout2StandardPSUOn.image withImage:presetImg3L2Standard];
                presetImg3L2Standard=nil;


                UIImage *presetImg3L2Premium = [APP_DEL getImage:[dict valueForKey:@"image3L2Premium"]];
                imgView3Layout2Premium.image = [ImageOperations imageByCombiningImages:imgView3Layout2Premium.image withImage:presetImg3L2Premium];
                imgView3Layout2PremiumPSUOn.image = [ImageOperations imageByCombiningImages:imgView3Layout2PremiumPSUOn.image withImage:presetImg3L2Premium];
                presetImg3L2Premium=nil;

            }



        }

        @autoreleasepool
        {
            UIImage *presetImg4Standard = [APP_DEL getImage:[dict valueForKey:@"image4Standard"]];
            mainImageView4.image=[ImageOperations imageByCombiningImages:mainImageView4.image withImage:presetImg4Standard];
            presetImg4Standard=nil;

            UIImage *presetImg5Standard = [APP_DEL getImage:[dict valueForKey:@"image5Standard"]];
            mainImageView5.image=[ImageOperations imageByCombiningImages:mainImageView5.image withImage:presetImg5Standard];
             presetImg5Standard=nil;
        }

        @autoreleasepool
        {
            UIImage *presetImg1Detail1 = [APP_DEL getImage:[dict valueForKey:@"image1DetailView1"]];
            imgView1Detail.image = [ImageOperations imageByCombiningImages:imgView1Detail.image withImage:presetImg1Detail1];
             presetImg1Detail1=nil;

            UIImage *presetImg2Detail2 = [APP_DEL getImage:[dict valueForKey:@"image2DetailView2"]];
            imgView2Detail.image = [ImageOperations imageByCombiningImages:imgView2Detail.image withImage:presetImg2Detail2];
            presetImg2Detail2=nil;

             UIImage *presetImg3Detail3 = [APP_DEL getImage:[dict valueForKey:@"image3DetailView3"]];
            imgView3Detail.image = [ImageOperations imageByCombiningImages:imgView3Detail.image withImage:presetImg3Detail3];
            presetImg3Detail3=nil;

        }


        [self setMaterialReflection];

        NSString *strShowIndicator = [dict valueForKey:@"hideIndicator"];
        if ([strShowIndicator isEqualToString:@"YES"]) {
            [appDelegate hideIndicator];
        }



    }
}

the issue is this when above method called the memory usage goes beyond 400 mb & i receive 2 - 3 times received memory warning message in console & my app is crashed after that.

in runtime when above method in execution memory always increasing even as you see in above method i have nil all the uiimage objects & also used @autorelese block but memory is not releasing.

i want to decrease memory after each call of following method.

  • (UIImage*)imageByCombiningImages:(UIImage*)firstImage withImage:(UIImage*)secondImage

in above method i am combining two images in to one image & all images in my projects are of average size of 2 MB.

i have decreased size of images to 2 mb average before it was of 4 mb, if i reduce images resolution than quality of app is decreasing & that is not what i want.

i have done a google about it & tried to find solution on stack overflow also but still got nothing, so please guide me through this issue.

please suggest me how can i improve method of image combining ? & how can i optimize above methods ?,so that memory usage can be reduced & my app can run smoothly.

2
You said that you had memory-leaks (cf. tag), but what line exactly is causing memory leak? What does say Instruments?Larme
How big are the images in pixels? That they are stored (as JPEGs, PNGs?) at a size of 2 MB doesn't mean that they take up 2 MB when in memory...Wildaker

2 Answers

1
votes

One thing I can suggest is don't draw each and every time to capture the image from graphic context.

Try the following steps:

  1. Pass the dictionary to the method imageByCombiningImages:
  2. Calculate the frame
  3. Iterate all the images from dictionary and draw
  4. Capture the final image only once.

Edit 1:

Edited Method:

    + (UIImage*)imageByCombiningImages:(NSDictionary *)imageDict
    {
     UIImage *image = nil;

            CGSize newImageSize = CGSizeZero;
    NSArray *allImages = [imageDict allValues];
    for (UIImage *image in allImages) {
    newImageSize  = CGSizeMake(MAX(image.size.width, newImageSize.size.width), MAX(image.size.height, newImageSize.size.height));
    }


            if (UIGraphicsBeginImageContextWithOptions != NULL)
            {
                UIGraphicsBeginImageContextWithOptions(newImageSize, NO, [[UIScreen mainScreen] scale]);
            }
            else
            {
                UIGraphicsBeginImageContext(newImageSize);
            }

    for (UIImage *image in allImages) {
            [image drawAtPoint:CGPointMake(roundf((newImageSize.width-image.size.width)/2), roundf((newImageSize.height-image.size.height)/2))];
    }

image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;

    }

Edit 2:

when you create image using UIGraphicsGetImageFromCurrentImageContext(), internally cg raster data is created by the core graphics to create the bitmap image. it will consume more memory.

you can avoid memory warning by delaying sequential method calls to merge the image.

Edit 3:

Modified image merge method:

- (void)imageByCombiningImages:(UIImage*)firstImage withImage:(UIImage*)secondImage
{

        UIImage *image = nil;

        CGSize newImageSize = CGSizeMake(MAX(firstImage.size.width, secondImage.size.width), MAX(firstImage.size.height, secondImage.size.height));
        if (UIGraphicsBeginImageContextWithOptions != NULL)
        {
            UIGraphicsBeginImageContextWithOptions(newImageSize, NO, [[UIScreen mainScreen] scale]);
        }
        else
        {
            UIGraphicsBeginImageContext(newImageSize);
        }

        [firstImage drawAtPoint:CGPointMake(roundf((newImageSize.width-firstImage.size.width)/2), roundf((newImageSize.height-firstImage.size.height)/2))];

        firstImage =nil;
        [secondImage drawAtPoint:CGPointMake(roundf((newImageSize.width-secondImage.size.width)/2), roundf((newImageSize.height-secondImage.size.height)/2))];


        secondImage=nil;

        image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

[self performSelector:@selector(merge:) withObject:image afterDelay:1]; // modify the delay which will work out for you

}

Add all the image keys in array and call the method,

- (void)merge:(UIImage *)image {

    switch (rowIndex) {
        case 0: {
            UIImage *presetImg1L1Standard = [APP_DEL getImage:[dict valueForKey:[imagesArray objectAtIndex:rowIndex]]];
            switch (sectionIndex) {
                case 0:
                    [ImageOperations imageByCombiningImages:imgView1Layout1Standard.image withImage:presetImg1L1Standard];
                    sectionIndex++;
                    break;
                  case 1:
                    imgView1Layout1Standard.image = image;
                    [ImageOperations imageByCombiningImages:imgView1Layout1StandardPSUOn.image withImage:presetImg1L1Standard];
                    rowIndex ++;
                    sectionIndex = 0;
                    break;
                default:
                    break;
            }
        }
            break;
        case 1: {
            UIImage *image = [APP_DEL getImage:[dict valueForKey:[imagesArray objectAtIndex:rowIndex]]];
            switch (sectionIndex) {
                case 0:
                    imgView1Layout1StandardPSUOn.image = image;
                    [ImageOperations imageByCombiningImages:imgView1Layout1Premium.image withImage:image];
                    sectionIndex++;
                    break;
                case 1:
                    imgView1Layout1Premium.image = image;
                    [ImageOperations imageByCombiningImages:imgView1Layout1Premium.image withImage:presetImg1L1Premium];
                    rowIndex ++;
                    sectionIndex = 0;
                    break;
                default:
                        break;
                }
            }
                break;
// ................. and so on
            default:
                break;
        }
    }

hope this will give you an idea.

0
votes

I would try to do the calculations on GPU, for instance using one of GPUImage's blend filters.