UIGraphicsBeginPDFPage() randomly crashes on 64bit

2019-05-01 19:50发布

问题:

I'm struggling with a pdf export method that's was running just fine until I ported the app to the arm64 achitecture.

Bacisally, the method opens an existing PDF, it creates a new pdf file, and draws the content of the first pdf into the newly created one before adding more content pages.

When the method tries to create a new pdf page to the document (after the first pdf was integrated to the new pdf) the app crashes with a EXC_BAD_ACCESS warning on UIGraphicsBeginPDFPage(); call .

It only happens with some PDF files, not all and only on 64 bit devices.

Here's the stacktrace which shows the CGPDFSecurityManagerCreateDecryptor () call which I couldn't find what it does.

Thread 14Queue : NSOperationQueue 0x14f6dd3a0 :: NSOperation 0x17504a470 (serial)
#0     0x00000001838aeee4 in CGPDFSecurityManagerCreateDecryptor ()
#1     0x00000001838d1004 in pdf_filter_chain_create ()
#2     0x0000000183831e00 in CGPDFStreamCreateFilterChain ()
#3     0x000000018383226c in chain_get_bytes ()
#4     0x0000000183b5e0ac in unpackImageRow ()
#5     0x0000000183b5dfd4 in PDFImageEmitData ()
#6     0x0000000183b5f684 in emit_image ()
#7     0x0000000183b5ef9c in PDFImageEmitDefinition ()
#8     0x0000000183464584 in __CFSetApplyFunction_block_invoke ()
#9     0x00000001834643bc in CFBasicHashApply ()
#10     0x00000001834642e4 in CFSetApplyFunction ()
#11     0x0000000183b5fa9c in PDFImageSetEmitDefinitions ()
#12     0x0000000183b590c0 in emit_page_resources(PDFDocument*) ()
#13     0x0000000183b5904c in PDFDocumentEndPage ()
#14     0x0000000183b57cf0 in pdf_EndPage ()
#15     0x0000000187fda904 in UIGraphicsBeginPDFPageWithInfo ()
#16     0x00000001002093e8 in -[ExportTools renderPdfContentToContext:forPlanVersion:]
#17     0x00000001001fba60 in -[ExportTools generatePdfReportWithOptions:]
#18     0x00000001000f7eb4 in -[DetailViewController generatePdfAndShowModalOpenWithAppWithOptions:]
#19     0x00000001835883c0 in __invoking___ ()
#20     0x0000000183486138 in -[NSInvocation invoke] ()
#21     0x000000018443ba20 in -[NSInvocationOperation main] ()
#22     0x000000018437c61c in -[__NSOperationInternal _start:] ()
#23     0x000000018443e26c in __NSOQSchedule_f ()
#24     0x000000010105cdf0 in _dispatch_client_callout ()
#25     0x0000000101067854 in _dispatch_queue_drain ()
#26     0x0000000101060120 in _dispatch_queue_invoke ()
#27     0x000000010106975c in _dispatch_root_queue_drain ()
#28     0x000000010106af18 in _dispatch_worker_thread3 ()
#29     0x00000001945012e4 in _pthread_wqthread ()

If you have any idea about this crash, your help would be greatly appreciated, been one day trying everything to fix this and beggening to wonder if it's not a UIKit bug...

Thanks

回答1:

I had a crash on the CGPDFSecurityManagerCreateDecryptor method on 64-devices only with the following code:

CGPDFDocumentRelease(pdf);
CGDataProviderRelease(provider);
UIGraphicsEndPDFContext();

CGPDFSecurityManagerCreateDecryptor would get called when ending the context. The crash went away when I ended the context BEFORE releasing the document and provider.

UIGraphicsEndPDFContext();
CGPDFDocumentRelease(pdf);
CGDataProviderRelease(provider);


回答2:

I've been struggling with this same issue too, and while Bill's answer gave me the clue I had to do it a little bit differently. In my situation there are a variable number of source PDFs that get copied into the target PDF so I can't just simply move UIGraphicsEndContext before CGPDFDocumentRelease. The code structure looks roughly like this:

UIGraphicsBeginPDFContextToFile(...);
// ...
for each attachment pdf {
    srcPdf = CGPDFDocumentCreateWithURL(...); // open source PDF
    // ...
    UIGraphicsBeginPDFPageWithInfo(...); // new page in target PDF, this randomly crashes
    // ...
    CGPDFDocumentRelease(srcPdf); // close source PDF
}
// ...
UIGraphicsEndPDFContext();

So instead I tried capturing the references to all the source PDFs it used and releasing them all after the rest of the target PDF is done, much later in the code. This is a kind of ugly because it moves the responsibility far away and holds all the memory until the end instead of releasing after each one is rendered... BUT it does appear to work! It's hard to say definitively since it's been a random crash but I haven't seen it since and I've been hammering it a lot trying to get it reoccur.

pdfRefs = [[NSPointerArray alloc] init];
UIGraphicsBeginPDFContextToFile(...);
// ...
for each attachment pdf {
    srcPdf = CGPDFDocumentCreateWithURL(...); // open source PDF
    // ...
    UIGraphicsBeginPDFPageWithInfo(...); // new page in target PDF, this randomly crashes
    // ...
    [pdfRefs addPointer:srcPdf]; // store for later closing
}
// ...
UIGraphicsEndPDFContext();
for each srcPdf in pdfRefs {
    CGPDFDocumentRelease(srcPdf); // close it here
}