You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 08:40:31