CGContextDrawPDFPage memory Leak - App Crash

2020-02-13 14:07发布

问题:

When I analyze my app with Instruments, I found out that data allocated by CGContextDrawPDFPage is not released immediately. App getting crash because of CGContextDrawPDFPage.

Hello here is my code for drawing pdf in CATiledlayer

      - (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)context
    {
    if (_PDFPageRef == nil) {
        return;
    }
    CGPDFPageRef drawPDFPageRef = NULL;
    CGPDFDocumentRef drawPDFDocRef = NULL;

    @synchronized(self) // Briefly block main thread
    {
        drawPDFDocRef = CGPDFDocumentRetain(_PDFDocRef);
        if( _PDFPageRef != (__bridge CGPDFPageRef)([NSNull null]) )
            drawPDFPageRef = CGPDFPageRetain(_PDFPageRef);
        else
            return;
    }

    //CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 0.0f);
    //CGContextFillRect(context, CGContextGetClipBoundingBox(context));

    if (drawPDFPageRef != NULL) // Render the page into the context
    {
        CGFloat boundsHeight = viewBounds.size.height;

        if (CGPDFPageGetRotationAngle(drawPDFPageRef) == 0)
        {
            CGFloat boundsWidth = viewBounds.size.width;

            CGRect cropBox = CGPDFPageGetBoxRect(drawPDFPageRef, kCGPDFCropBox);
            int pageRotation = CGPDFPageGetRotationAngle(drawPDFPageRef);

            CGSize pageVisibleSize = CGSizeMake(cropBox.size.width, cropBox.size.height);
            if ((pageRotation == 90) || (pageRotation == 270) ||(pageRotation == -90)) {
                pageVisibleSize = CGSizeMake(cropBox.size.height, cropBox.size.width);
            }

            float scaleX = boundsWidth / pageVisibleSize.width;
            float scaleY = boundsHeight / pageVisibleSize.height;
            float scale = scaleX < scaleY ? scaleX : scaleY;

            // Offset relative to top left corner of rectangle where the page will be displayed
            float offsetX = 0;
            float offsetY = 0;

            float rectangleAspectRatio = boundsWidth / boundsHeight;
            float pageAspectRatio = pageVisibleSize.width / pageVisibleSize.height;

            if (pageAspectRatio < rectangleAspectRatio) {
                // The page is narrower than the rectangle, we place it at center on the horizontal
                offsetX = (boundsWidth - pageVisibleSize.width * scale) / 2;
            }
            else {
                // The page is wider than the rectangle, we place it at center on the vertical
                offsetY = (boundsHeight - pageVisibleSize.height * scale) / 2;
            }

            CGPoint point = CGPointMake(offsetX, offsetY);

            //CGRect cropBox = CGPDFPageGetBoxRect(drawPDFPageRef, kCGPDFCropBox);
            int rotate = CGPDFPageGetRotationAngle(drawPDFPageRef);

            //CGContextSaveGState(context);

            // Setup the coordinate system.
            // Top left corner of the displayed page must be located at the point specified by the 'point' parameter.
            CGContextTranslateCTM(context, point.x, point.y);

            // Scale the page to desired zoom level.
            CGContextScaleCTM(context, scale , scale);

            // The coordinate system must be set to match the PDF coordinate system.
            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:
                    CGContextTranslateCTM(context, cropBox.size.height, cropBox.size.width);
                    CGContextRotateCTM(context, M_PI / 2);
                    CGContextScaleCTM(context, -1, 1);
                    break;
            }

            // The CropBox defines the page visible area, clip everything outside it.
            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);
          //               CGContextSetInterpolationQuality(context, kCGInterpolationMedium);
            CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);

            if(context != nil && context != (__bridge CGContextRef)([NSNull null]))
            {
                CGContextDrawPDFPage(context, drawPDFPageRef);
                //CGContextRestoreGState(context);
            }


        }
        else // Use CGPDFPageGetDrawingTransform for pages with rotation (AKA kludge)
        {
            CGContextTranslateCTM(context, 0.0f, boundsHeight); CGContextScaleCTM(context, 1.0f, -1.0f);

            CGContextConcatCTM(context, CGPDFPageGetDrawingTransform(drawPDFPageRef, kCGPDFCropBox, viewBounds, 0, true));
        }

            //CGContextDrawPDFPage(context, drawPDFPageRef);
    }

    CGPDFPageRelease(drawPDFPageRef); // Cleanup
    CGPDFDocumentRelease(drawPDFDocRef);
}

回答1:

This is by design. To allow faster re-draws, the CGPDFDocumentRef caches page resources.

The only way to empty this cache is to release and re-open the CGPDFDocumentRef.