swiftmemory-managementcore-graphics

Memory leak when resizing images


I have a method converting PDF to images (every page converts to UIImage) with resizing each of them. Method is calling at custom queue with QOS = background.

When I try to process PDF with more then 90 pages, I got an error and my app crashes:

Message from debugger: Terminated due to memory issue

Here is my method:

func convertPDFToImages(pdfURL: URL) -> ContiguousArray<UIImage>? {
    guard let pdfDocument = PDFDocument(url: pdfURL) else {
      return nil
    }

    var images: ContiguousArray<UIImage> = []
    autoreleasepool {
      for pageNum in 0..<pdfDocument.pageCount {
        if let pdfPage = pdfDocument.page(at: pageNum) {
          let pdfPageSize = pdfPage.bounds(for: .mediaBox)
          let renderer = UIGraphicsImageRenderer(size: pdfPageSize.size)
          
          var image = renderer.image { ctx in
            UIColor.white.set()
            ctx.fill(pdfPageSize)
            ctx.cgContext.translateBy(x: 0.0, y: pdfPageSize.size.height)
            ctx.cgContext.scaleBy(x: 1.0, y: -1.0)
            
            pdfPage.draw(with: .mediaBox, to: ctx.cgContext)
          }
          
          image = scalePreservingAspectRatio(image, newWidth: newWidth)             
          
          images.append(image)
        }
      }
    }

    return images
  }

func scalePreservingAspectRatio(_ image: UIImage, newWidth: CGFloat) -> UIImage {
    let scaleFactor = newWidth / image.size.width
    let scaledImageSize = CGSize(
      width: newWidth,
      height: image.size.height * scaleFactor
    )
    let renderer = UIGraphicsImageRenderer(
      size: scaledImageSize
    )

    let scaledImage = renderer.image { _ in
      image.draw(in: CGRect(
        origin: .zero,
        size: scaledImageSize
      ))
    }

    return scaledImage
  }

What I've tryed:

As I understand, the problem is with creating UIGraphicsImageRenderer and intermediate image instance so many times enter image description here


Solution

  • ok, I got an answer for my own question)

    first of all, based on this answer I've moved autorelease pool inside the loop

    and as a second step, I add the format to the renderer with scale factor 1 (based on this great explanation)

              let format = UIGraphicsImageRendererFormat()
              format.scale = 1
    
              let renderer = UIGraphicsImageRenderer(size: pdfPageSize.size, format: format)