Nick Kirsten
Nick Kirsten

Reputation: 1187

Tiled PDF view is leaking memory (non ARC)

So I work on a very large and very old legacy project that was coded about 5 years ago in non-ARC objective c code. I took over from the previous developers and now have to maintain this beast.

The app allows users to read PDF pages that are downloaded as separate files. The problem is the view where the PDFs are displayed on the screen is leaking memory badly. Simple paging for about 5 minutes results in about 400 megs of memory consumed.

So when profiling the app I discovered the line of code that seems to be the issue. The previous developer even left a comment: "TODO DANNY leak here 100%"... Thanks Danny...

Anyway I have tried many things and for the life of me can't understand why this is leaking memory. Most of my work and experience is with ARC projects so I am struggling to understand why this code is leaking.

The UIView subview class looks as follows:

- (void)dealloc {
    self.pauseDraw = YES;
    CGPDFPageRelease(pdfPage);  
    CGPDFDocumentRelease(pdfDocumentRef);
    [super dealloc];
}

- (id)initWithFrame:(CGRect)frame andScale:(CGFloat)scale{
    if ((self = [super initWithFrame:frame])) {        
        CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];

        if ([[[UIDevice currentDevice] modelName] isEqualToString:@"iPad 3G"]) {
            tiledLayer.tileSize = CGSizeMake(1024, 1024);
            tiledLayer.levelsOfDetail = 4;
            tiledLayer.levelsOfDetailBias = 4;
        } else {
            tiledLayer.levelsOfDetail = 4;
            tiledLayer.levelsOfDetailBias = 4;
            tiledLayer.tileSize = CGSizeMake(2048, 2048);
        }
        myScale = scale;
    }
    return self;
}

+ (Class)layerClass {
    return [CATiledLayer class];
}

- (void)setPage:(CGPDFPageRef)newPage andDocument:(CGPDFDocumentRef)docRef {
    CGPDFPageRelease(pdfPage);
    CGPDFDocumentRelease(pdfDocumentRef);
    self->pdfPage = CGPDFPageRetain(newPage);
    self->pdfDocumentRef = CGPDFDocumentRetain(docRef);
}

- (void) releasePDFsFromMemory {
    CGPDFPageRelease(pdfPage);
    CGPDFDocumentRelease(pdfDocumentRef);
}

-(void)drawRect:(CGRect)r {}

-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {

    if(!self.superview){
        return;
    }

    if (pauseDraw) {
        return;
    }   

    @autoreleasepool {
        CGRect cropBox = CGPDFPageGetBoxRect(pdfPage, kCGPDFCropBox);
        int rotate = CGPDFPageGetRotationAngle(pdfPage);

        CGContextSaveGState(context);   
        CGContextTranslateCTM(context, 0, 0);
        CGContextScaleCTM(context, myScale, myScale);

        switch (rotate) {
            case 0:
                CGContextTranslateCTM(context, 0, cropBox.size.height);
                CGContextScaleCTM(context, 1, -1);
                break;
            case 90:
                CGContextScaleCTM(context, 1, -1);
                CGContextRotateCTM(context, -M_PI / 2);
                break;
            case 180:
            case -180:
                CGContextScaleCTM(context, 1, -1);
                CGContextTranslateCTM(context, cropBox.size.width, 0);
                CGContextRotateCTM(context, M_PI);
                break;
            case 270:
            case -90:
                self.frame = CGRectMake(0, 0, 768, 1004);
                CGContextTranslateCTM(context, cropBox.size.height, cropBox.size.width);
                CGContextRotateCTM(context, M_PI / 2);
                CGContextScaleCTM(context, -1, 1);
                break;
        }

        CGRect clipRect = CGRectMake(0, 0, cropBox.size.width, cropBox.size.height);
        CGContextAddRect(context, clipRect);
        CGContextClip(context);

        CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
        CGContextFillRect(context, clipRect);

        CGContextTranslateCTM(context, -cropBox.origin.x, -cropBox.origin.y);

        CGContextSetInterpolationQuality(context, kCGInterpolationHigh); 
        CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);

        // TODO DANNY leak here 100% ->
        CGContextDrawPDFPage(context, pdfPage);

        CGContextRestoreGState(context);
    }    
}

Any help will be much appreciated!

Thanks in advance, Eish

Upvotes: 0

Views: 494

Answers (1)

Nick Kirsten
Nick Kirsten

Reputation: 1187

So I managed to solve the issue.

The problem was not with the code I pasted above (apologies for being misleading), the problem was that in the parent view's dealloc method the previous developer was not removing the tiledPDF views from the superview and was not setting the variable to nil after calling release.

I discovered this by putting an NSLog in the dealloc method and saw the parent view was deallocating, but the 2 tiled PDF subviews were not deallocating at all.

After removing them from the superview and setting the variables to nil both dealloc methods were being fired and the app memory usage does not permanently rise as you page!

Upvotes: 1

Related Questions