iOS: Get "Received memory warning" after use of image created from PDF and used as background

The task: create an image from a PDF file as fast background preview for my PDF displayed with the CATiledLayer (slower, higher resolution). Problem: I get pretty fast an error warning on my iPad "Received memory warning. Level=1" and shortly after "Received memory warning. Level=2" .. then the app crashes.

- (void) drawSinglePage:(CALayer *)layer inContext:(CGContextRef)ctx {
    CGContextSaveGState(ctx);
    CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0);
    CGContextFillRect(ctx, CGContextGetClipBoundingBox(ctx));
    @synchronized(self) {

        // Draw PDF for HighRes
        CGPDFPageRef page = CGPDFDocumentGetPage(page1, 1);
        CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
        CGFloat scaleRatio = 960/pageRect.size.height;
        CGFloat yOffset = ((960-pageRect.size.height)/scaleRatio)+960;

        CGContextTranslateCTM(ctx, -(layer.bounds.size.width-pageRect.size.width)/2, yOffset);
        CGContextScaleCTM(ctx, scaleRatio, -scaleRatio);
        CGContextConcatCTM(ctx, CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, layer.bounds, 0, true));
        CGContextDrawPDFPage(ctx, page);

        // Draw Background-Image as fast preview (PROBLEM HERE!!)
        UIGraphicsBeginImageContext(layer.bounds.size);
        CGContextRef context = UIGraphicsGetCurrentContext();

        CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
        CGRect prob = CGContextGetClipBoundingBox(context);
        CGContextFillRect(context, prob);

        CGContextSaveGState(context);
        CGContextSetInterpolationQuality(context, kCGInterpolationLow);
        CGContextTranslateCTM(context, -(layer.bounds.size.width-pageRect.size.width)/2, yOffset);
        CGContextScaleCTM(context, scaleRatio, -scaleRatio);
        CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(page, kCGPDFCropBox, CGRectMake(0.0, 0.0, layer.bounds.size.width, layer.bounds.size.height), 0, true));
        CGContextDrawPDFPage(context, page);
        CGContextRestoreGState(context);
        UIImage *back = UIGraphicsGetImageFromCurrentImageContext();
        NSData *jpegDataFile = UIImageJPEGRepresentation(back, 0.2f);
        UIImage *smBack = [[UIImage alloc] initWithData:jpegDataFile];      
        UIGraphicsEndImageContext();

        // Now set the subview
        UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:smBack];
        backgroundImageView.frame = CGRectMake(0.0, 0.0, smBack.size.width, smBack.size.height);
        backgroundImageView.contentMode = UIViewContentModeTopLeft;
        backgroundImageView.opaque = YES;
        [self.view addSubview:backgroundImageView];
        [self.view sendSubviewToBack:backgroundImageView];
        [backgroundImageView release];

    }
    CGContextRestoreGState(ctx);
}

}

Note: if I don't set the background image view everything is ok - so pretty sure I have to release something, but I tried everything. At the moment I have no Idea. Note: the "CGPDFDocumentRelease(page1)" will be called in the - (void)dealloc function of this ViewController.

Answers


I don't know how many times your drawSinglePage:inContext: method is being called, but it will create a new image view with the image in it every time while not keeping a reference to it. This means a second call will increase memory usage, while only one image is visible.

So if this method is called multiple times you should either reuse the image view (set imageView.image = nil), or remove the earlier created image view before creating a new one. This will release the image already in memory which might prevent a memory warning during drawing of the page.

Also keep in mind that drawing a pdf page can spike the memory usage over 20 mb each time. So depending on how much memory your application is using already this alone could cause the memory warning. Keep memory usage low and do not try to draw more than one page at the same time.


Look into https://github.com/mindbrix/UIImage-PDF (BSD?), they're doing a good job in rendering a PDF into an UIImage.

When looking at your code, I see that you're not freeing the CGPDFDocument after drawing. Note that, depending on the document, CGPDFDocument might take considerable amount of memory, and compressing a retina iPad image with UIImageJPEGRepresentation also needs a lot of memory. You can avoid that memory pressure if you free all your stuff (CGPDFPage, CGPDFDocument) directly after drawing.

Also note that CGPDFDocument has an internal cache that can only be cleared if it's destroyed and recreated. Do that after receiving a memory warning, or you will crash eventually. (if the PDF is sufficiently complex.)


Need Your Help

calling [myString release] does NOT decrement [myString retainCount]

iphone objective-c ipad nsstring

I have the following situation, which seems to cause my iPad application to leak memory.

choose proper service, spring

java spring

I have a web application in java using spring.