gpt4 book ai didi

ios - 在 iOS 中从 HTML 创建 PDF 时出现异常

转载 作者:塔克拉玛干 更新时间:2023-11-02 21:58:07 24 4
gpt4 key购买 nike

我有一个过程采用 HTML 字符串数组并使用以下方法逐页构建 PDF:

let printPageRenderer = ReportPrintPageRenderer(withTemplate: self.template)

var pageIndex = 0
data.forEach { (htmlPage) in
let printFormatter = UIMarkupTextPrintFormatter(markupText: htmlPage)

printPageRenderer.addPrintFormatter(printFormatter, startingAtPageAt: pageIndex)
pageIndex += 1
}

let pdfData = drawPDFUsingPrintPageRendererNoImages(printPageRenderer: printPageRenderer)

drawPDFUsing... 方法使用了我多次描述过的标准方法:

let data = NSMutableData()

UIGraphicsBeginPDFContextToData(data, CGRect.init(x: 0, y: 0, width: template.pageWidth, height: template.pageHeight), nil)

printPageRenderer.prepare(forDrawingPages: NSMakeRange(0, printPageRenderer.numberOfPages))

let bounds = UIGraphicsGetPDFContextBounds()

for i in 0...((printPageRenderer.numberOfPages - 1 < 0) ? 0 : printPageRenderer.numberOfPages - 1) {
UIGraphicsBeginPDFPage()
printPageRenderer.drawPage(at: i, in: bounds)
}

UIGraphicsEndPDFContext()

return data

整个过程非常完美,直到我找到大约 130 页长的 PDF 文档。在模拟器中,我可以毫无问题地打印 250 多页的文档。但是,对于实际设备(iPhone x 或 iPad pro)上的相同数据,data.forEach 循环一直运行到 pageIndex 达到 130 左右,然后它失败并在该行显示以下信息:

let printFormatter = UIMarkupTextPrintFormatter(markupText: htmlPage)
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1aa2b0330)
warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available.

没有产生其他异常

内存使用率仍然可以,虽然它已经飙升,但考虑到我正在做的事情,它似乎并不过分。另外,在模拟器中运行时会这样增加,一般峰值只有300-400MB左右。

enter image description here

我尝试了以下方法来解决这个问题,但都没有成功:

  1. 将进程的各个部分包装在 autoreleasepool 中
  2. 通过在处理数据时随机化数据来确保它不是我的 HTML 数据字符串。无论顺序如何,它总是在 130 条记录后失败
  3. 切换到 iOS13
  4. 更改将每一页作为单独的 PDF 写入磁盘的流程。不管我怎么做,创建130页后总是失败

我不知道这是怎么回事,也不知道如何解决这个问题。任何想法或帮助将不胜感激。

根据评论中的建议,这是在调试器中运行 bt 的跟踪:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1aa326a14)
frame #0: 0x00000001aa326a14 WebCore`bmalloc::IsoAllocator<bmalloc::IsoConfig<88u> >::allocateSlow(bool) + 252
frame #1: 0x00000001aaaf7f48 WebCore`WebCore::RenderText::createTextBox() + 28
frame #2: 0x00000001aab06e90 WebCore`WebCore::RenderTextLineBoxes::createAndAppendLineBox(WebCore::RenderText&) + 40
frame #3: 0x00000001aa9e2c50 WebCore`WebCore::RenderBlockFlow::constructLine(WebCore::BidiRunList<WebCore::BidiRun>&, WebCore::LineInfo const&) + 344
frame #4: 0x00000001aa9e5944 WebCore`WebCore::RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned int, WebCore::BidiRunList<WebCore::BidiRun>&, WebCore::InlineIterator const&, WebCore::LineInfo&, WebCore::VerticalPositionCache&, WebCore::BidiRun*, WTF::Vector<WebCore::WordMeasurement, 64ul, WTF::CrashOnOverflow, 16ul>&) + 100
frame #5: 0x00000001aa9e825c WebCore`WebCore::RenderBlockFlow::layoutRunsAndFloatsInRange(WebCore::LineLayoutState&, WebCore::BidiResolverWithIsolate<WebCore::InlineIterator, WebCore::BidiRun, WebCore::BidiIsolatedRun>&, WebCore::InlineIterator const&, WebCore::BidiStatus const&, unsigned int) + 4576
frame #6: 0x00000001aa9e5e2c WebCore`WebCore::RenderBlockFlow::layoutRunsAndFloats(WebCore::LineLayoutState&, bool) + 920
frame #7: 0x00000001aa9ea03c WebCore`WebCore::RenderBlockFlow::layoutLineBoxes(bool, WebCore::LayoutUnit&, WebCore::LayoutUnit&) + 1800
frame #8: 0x00000001aa9cd5cc WebCore`WebCore::RenderBlockFlow::layoutBlock(bool, WebCore::LayoutUnit) + 1056
frame #9: 0x00000001aaadad84 WebCore`WebCore::RenderTableCell::layout() + 196
frame #10: 0x00000001aaae8794 WebCore`WebCore::RenderTableRow::layout() + 252
frame #11: 0x00000001aaaea914 WebCore`WebCore::RenderTableSection::layout() + 844
frame #12: 0x00000001aaacf744 WebCore`WebCore::RenderTable::layout() + 1916
frame #13: 0x00000001aa9cf578 WebCore`WebCore::RenderBlockFlow::layoutBlockChild(WebCore::RenderBox&, WebCore::RenderBlockFlow::MarginInfo&, WebCore::LayoutUnit&,
.....

如果我将它剥离回最基本的要素,甚至不尝试创建 PDF:

func exportHTMLContentToPDFNoImages(reportName name:String, fromHTMLData data: [String]) throws -> URL? {
data.shuffled().forEach { (htmlPage) in
autoreleasepool { () -> Void in
let _ = UIMarkupTextPrintFormatter(markupText: htmlPage)
}
}
return nil
}

它在第 130 次循环后仍然崩溃。用一些简单的 html 替换 htmlPage,比如

<html><body>Hi</body></html>

工作正常。所以我创建的表中一定有什么东西导致了这个问题。

更新

我能够将其简化为一个非常简单的示例,向我证明这是 iOS 中的一个错误。我也最终向苹果报告了它。幸运的是,这个错误似乎已在 13.1 中得到修复,因此即使 iOS 12 存在问题, future 仍有前进的道路。

最佳答案

要么内存不足,要么堆已损坏。我的猜测是,尽管您有内存使用情况屏幕截图,但您的内存已用完。

这个 EXC_BREAKPOINT 出现在 WebKit 的 bmalloc::IsoAllocator 中。

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1aa326a14)
frame #0: 0x00000001aa326a14 WebCore`bmalloc::IsoAllocator<bmalloc::IsoConfig<88u> >::allocateSlow(bool) + 252
frame #1: 0x00000001aaaf7f48 WebCore`WebCore::RenderText::createTextBox() + 28

Here is the line这引发了异常(注意这是来自 WebKit trunk,它可能与您手机上运行的版本不同):


template<typename Config>
BNO_INLINE void* IsoAllocator<Config>::allocateSlow(bool abortOnFailure)
{
...

return m_freeList.allocate<Config>([] () { BCRASH(); return nullptr; });
}

m_freeList.allocate 如果无法从它自己的列表分配,则运行 lambda(括号中的部分)中的内容。

BCRASH 引发您看到的异常。来自 BAssert.h :

// Crash with a SIGTRAP i.e EXC_BREAKPOINT.
// We are not using __builtin_trap because it is only guaranteed to abort, but not necessarily
// trigger a SIGTRAP. Instead, we use inline asm to ensure that we trigger the SIGTRAP.
#define BCRASH() do { \
BBreakpointTrap(); \
__builtin_unreachable(); \
} while (false)

此分配似乎是 WebKit 呈现您的 PDF 的一部分,但它可能会受到不断增长的 NSMutableData 的压力。

尝试使用 UIGraphicsBeginPDFContextToFile而不是 UIGraphicsBeginPDFContextToData 并查看崩溃是否仍然发生。 UIGraphicsBeginPDFContextToFile 的实现可能会将页面写入磁盘,而不是像您正在做的那样将整个结果一次性全部放入内存。

一旦切换到 UIGraphicsBeginPDFContextToFile,您就可以将 PDF 作为文件使用,或者,如果需要加载到 NSData 中,使用此方法使用内存映射文件:

let data = try Data(contentsOf: URL(fileURLWithPath: newPDFPath), options: .mappedIfSafe)

关于ios - 在 iOS 中从 HTML 创建 PDF 时出现异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57714080/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com