首页 > 解决方案 > iOS - 将 PDF 转换为图像时过度使用内存导致崩溃

问题描述

这就是我想要实现的目标:用户在 UIDocumentPickerViewController 中选择一些 PDF 文档后,将文档的每一页转换为 UIImage,将该 UIImage 保存到磁盘并将其文件 url 存储在一个imageUrls数组中。转换完所有文档后,我会将imageUrls数组传递给我的下一个 ViewController。

以下代码可以正常工作,但会导致内存 (RAM) 的使用量急剧增加:我的应用程序在转换之前使用 92MB 内存,而当我尝试转换仅 25MB 的 PDF 文档时,它最多可以使用550MB 。虽然这在我的模拟器中运行,但它导致我用户的 iPhone XR 发生一些崩溃。我猜原因是过度使用内存。

以下是我进行转换的代码,如果有人可以帮助我确定问题所在,将不胜感激。

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        print("Import result: \(urls)"). // urls look like this [file:///Users/me/Library/Developer/CoreSimulator/Devices/.../my-document.pdf]
        
        let group = DispatchGroup() // use group to monitor the completion of converting all picked PDF documents
        var imageUrls = [URL]()  // the URL array to pass to the next ViewController

        for (docIndex, url) in urls.enumerated() {
            group.enter()
            // do conversion on global queue so UI is not blocked
            DispatchQueue.global(qos: .userInitiated).async {
                guard let pdfDocument = createPDFdocumentFromUrl(url) else { return } // helper method 1: createPDFdocumentFromUrl(_:URL)
                SVProgressHUD.show(withStatus: "Converting PDF \(docIndex + 1)") // show a spinner

                for page in (1...pdfDocument.numberOfPages) {
                    guard let image = pdfDocument.drawPage(page),  // helper method 2: CGPDFDocument.drawPage(_:Int)
                        let imageUrl = image.saveToDisk(fileName: "drawing-\(docIndex)-\(page).jpg") // helper method 3: UIImage.saveToDisk(fileName: String)
                        else { continue }
                    imageUrls.append(imageUrl)
                }
                group.leave()
            }
        }
        
        group.notify(queue: .main) {
            SVProgressHUD.dismiss() // dismiss spinner
            print("Pass imageUrls array to next ViewController")
        }
}

这是我在上面的代码中使用的 3 个辅助方法:

// helper method 1
func createPDFdocumentFromUrl(_ url: URL) -> CGPDFDocument? {
    CGPDFDocument(url as CFURL) ?? nil
}

extension CGPDFDocument {
    // helper method 2: convert the specified PDF page into UIImage
    func drawPage(_ page: Int) -> UIImage? {
        guard let page = self.page(at: page) else { return nil }
        
        let pageRect = page.getBoxRect(.mediaBox)
        let renderer = UIGraphicsImageRenderer(size: pageRect.size)
        let img = renderer.image { ctx in
            UIColor.white.set()
            ctx.fill(pageRect)

            ctx.cgContext.translateBy(x: 0.0, y: pageRect.size.height)
            ctx.cgContext.scaleBy(x: 1.0, y: -1.0)

            ctx.cgContext.drawPDFPage(page)
        }

        return img
    }
}


extension UIImage {
    /// helper method 3: save image in `/www` folder
    func saveToDisk(fileName: String) -> URL? {
        guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
        let wwwFolderUrl = documentsDirectory.appendingPathComponent("www")
        let fileURL = wwwFolderUrl.appendingPathComponent(fileName)
        guard let data = self.jpegData(compressionQuality: 1) else { return nil }
        
        //Checks if file exists, removes it if so.
        if FileManager.default.fileExists(atPath: fileURL.path) {
            do {
                try FileManager.default.removeItem(atPath: fileURL.path)
                print("Removed old image")
            } catch let removeError {
                print("couldn't remove file at path", removeError)
                return nil
            }
        }
        
        do {
            try data.write(to: fileURL)
        } catch let error {
            print("error saving file with error", error)
            return nil
        }
        
        return fileURL
    }
}

标签: iosswiftram

解决方案


推荐阅读