使用PDFKit合并PDF文档时的内存问题排查求助
PDF合并内存崩溃问题分析与修复
原代码的核心问题
- 内存累积无法释放:你用
PDFDocument加载整个PDF并复制所有页面到resultPDF,所有页面对象会被resultPDF持续持有,直到函数执行完毕。哪怕加了autoreleasepool,也只能释放pdfPart本身,复制后的页面内存依旧被占用,大PDF叠加起来直接触发内存崩溃。 - autoreleasepool范围不合理:当前的pool包裹了整个单PDF的处理循环,但真正占内存的页面对象已经被
resultPDF持有,不在这个pool的释放范围内,所以起不到缓解内存的作用。
优化方案:用Core Graphics逐页写入(低内存占用)
改用Core Graphics的底层API,不需要把所有页面都存在内存里,处理一页写一页,内存占用会大幅降低。
import CoreGraphics import PDFKit func combinePDFs(urls: [URL]) throws -> URL { let filename = "\(UUID().uuidString).pdf" let destUrl = try self.projectFileResolver.urlForFile(filename, ofType: .tmp, inProject: TmpProjectID) // 创建目标PDF的上下文 guard let context = CGContext(destUrl as CFURL, mediaBox: nil, nil) else { throw NSError(domain: "PDFCombineError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to create PDF context"]) } for url in urls { autoreleasepool { guard let pdf = CGPDFDocument(url as CFURL) else { return } let pageCount = pdf.numberOfPages for pageIndex in 1...pageCount { // CGPDF页面索引从1开始 guard let page = pdf.page(at: pageIndex) else { continue } // 获取页面尺寸 let mediaBox = page.getBoxRect(.mediaBox) context.beginPage(mediaBox: &mediaBox) // 绘制页面到目标上下文 context.saveGState() context.translateBy(x: 0, y: mediaBox.size.height) context.scaleBy(x: 1.0, y: -1.0) context.drawPDFPage(page) context.restoreGState() context.endPage() } // 显式关闭文档,帮助回收资源 pdf.close() } } // 结束上下文并写入文件 context.closePDF() return destUrl }
关键优化点说明
- 逐页处理+即时写入:用
CGPDFDocument逐页加载源PDF,绘制到目标上下文后就可以释放当前页的资源,不会在内存中累积所有页面。 - 缩小autoreleasepool范围:每个源PDF的处理都在独立的pool里,处理完后临时对象会被立即释放,进一步降低内存峰值。
- 底层API更高效:Core Graphics的PDF操作比PDFKit的
PDFDocument更轻量化,适合处理大文件或批量文件。
额外建议
- 如果必须用PDFKit(比如需要保留PDF的交互元素),可以尝试分批次写入:累积一定数量的页面后就写入临时文件,然后清空
resultPDF再继续处理下一批,避免内存持续增长。 - 监控内存使用:用Xcode的Memory Graph Debugger查看内存占用情况,确认是否有内存泄漏或对象未及时释放的问题。
内容的提问来源于stack exchange,提问作者Aymarick
相关产品推荐
相关产品推荐

