创建PDF时内存警告和崩溃 [英] Memory warning and crash when creating PDF

查看:104
本文介绍了创建PDF时内存警告和崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在生成大型PDF时,我的应用会向内存发出警告,然后在PDF的生成过程中崩溃。 PDF被绘制到网页视图中,当页面超过一定数量(取决于设备)时,我的内存不足

When genrating a large PDF, my app recives a memory a warning, then crashes during the generation process of the PDF. The PDF is drawn into a web view, when the pages get above a certain amount (depending on device) I run out of memory

到目前为止,我对这个问题的研究领先我理解我需要:

My research into this matter so far leads me to understand I need to:

UIGraphicsBeginPDFContextToData 更改为 IGraphicsBeginPDFContextToFile


  1. 创建临时文件的合理路径,

  1. Create a reasonable path to a temporary file,

将其交给函数,

将文件提供给webview加载。

Give the file to the webview to load.

完成后删除文件。

问题是,虽然我认为我(仅仅)在市政当局掌握它,但我不知道如何去实现这个,或完全理解它,以便在我的代码中实现它。对此问题的建议非常高兴

Problem is, while I think I grasp it (just) in pricipal, I dont know how to go about acheiving this, or fully understand it so as to impliment it within my code. Advice in this matter very much appricated

我也对任何其他想法停止内存崩溃

Im also open to any other ideas to stop the memory crash

@interface ICPDFPreviewController ()
@property (nonatomic, strong) Certificate *certificate;
@property (nonatomic, strong) NSData *pdfData;
@property (nonatomic) BOOL viewHasUnloaded;
- (void)generatePdf;
- (void)pdfDone:(NSData *)data;
- (NSData *)createPdfWithPages:(NSArray *)pages;
@end

@implementation ICPDFPreviewController
@synthesize certificate=_certificate;
@synthesize scrollView=_scrollView;
@synthesize webView=_webView;
@synthesize pdfData=_pdfData;
@synthesize viewHasUnloaded=_viewHasUnloaded;



- (void)generatePdf
 {
 NSMutableArray *pagesArray = [NSMutableArray array];

 if ([self.certificate.certificateType.title isEqualToString:@"Minor Works"]) {
[pagesArray addObject:[[ICPDFMinorWorksPage1 alloc] initWithCertificate:self.certificate]];
 [pagesArray addObject:[[ICPDFMinorWorksPage2 alloc] initWithCertificate:self.certificate]];

 } else if ([self.certificate.certificateType.title isEqualToString:@"EIC"]) {
[pagesArray addObject:[[ICPDFEICPage1 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICPage2 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICPage3 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICPage4 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICPage5 alloc] initWithCertificate:self.certificate]];
[self addDistributionBoardsToPagesArray:pagesArray];
ICPDFEICPageFinal *pageFinal = [[ICPDFEICPageFinal alloc]        initWithCertificate:self.certificate];
 pageFinal.pageNumber.text = [NSString stringWithFormat:@"%d", pagesArray.count+1];
[pagesArray addObject:pageFinal];

} else if ([self.certificate.certificateType.title isEqualToString:@"Domestic EIC"]) {
[pagesArray addObject:[[ICPDFDomesticEICPage1 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFDomesticEICPage2 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFDomesticEICPage3 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFDomesticEICPage4 alloc] initWithCertificate:self.certificate]];
[self addDistributionBoardsToPagesArray:pagesArray];
[pagesArray addObject:[[ICPDFDomesticEICPageFinal alloc] initWithCertificate:self.certificate]];

} else if ([self.certificate.certificateType.title isEqualToString:@"EICR"]) {
[pagesArray addObject:[[ICPDFEICRPage1 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICRPage2 alloc] initWithCertificate:self.certificate]];
[self addObservationsToPagesArray:pagesArray];
[self addDistributionBoardsToPagesArray:pagesArray];
[pagesArray addObject:[[ICPDFEICRInspection alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICRInspectionPage1 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICRInspectionPage2 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICRInspectionPage3 alloc] initWithCertificate:self.certificate]];
[pagesArray addObject:[[ICPDFEICRPageFinal alloc] initWithCertificate:self.certificate]];
 }

// Set page count on all pages
int pageNumber = 0;
for (ICCertificateComponent *page in pagesArray) {
page.pageNumber.text = [NSString stringWithFormat:@"%d", ++pageNumber];
page.pageCount.text = [NSString stringWithFormat:@"%d", pagesArray.count];
}

 NSData *pdfData = [self createPdfWithPages:pagesArray];
[self performSelectorOnMainThread:@selector(pdfDone:) withObject:pdfData waitUntilDone:YES];

 }

- (void)pdfDone:(NSData *)data
 {
 self.pdfData = data;
[self.webView loadData:self.pdfData MIMEType:@"application/pdf" textEncodingName:@"utf-8"      baseURL:nil];
 [ICUtils removeProgressView];
 }

 - (NSData *)createPdfWithPages:(NSArray *)pages
  {
 // Creates a mutable data object for updating with binary data, like a byte array
 NSMutableData *pdfData = [NSMutableData data];

 ICCertificateComponent *firstPage = [pages objectAtIndex:0];

UIGraphicsBeginPDFContextToData(pdfData, firstPage.contentView.bounds, nil);

 for (int i = 0; i < pages.count; i++) {
 ICCertificateComponent *thisPage = [pages objectAtIndex:i];
 UIGraphicsBeginPDFPageWithInfo(thisPage.contentView.bounds, nil);


 CGContextRef pdfContext = UIGraphicsGetCurrentContext();
 [thisPage.contentView.layer renderInContext:pdfContext];
 }

 UIGraphicsEndPDFContext();

 return pdfData;
}

- (void)addDistributionBoardsToPagesArray:(NSMutableArray *)pagesArray
{
int pageCount = pagesArray.count;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdAt"       ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; 
NSArray *boards = [self.certificate.distributionBoards      sortedArrayUsingDescriptors:sortDescriptors];
 for (DistributionBoard *thisBoard in boards) {
DebugLog(@"Creating a board page");
ICPDFDistributionBoard *boardPage = [[ICPDFDistributionBoard alloc]        initWithDistributionBoard:thisBoard];
boardPage.pageNumber.text = [NSString stringWithFormat:@"%d", ++pageCount];
DebugLog(@"Page number is %d", pageCount);
[pagesArray addObject:boardPage];

NSSortDescriptor *circuitDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdAt"     ascending:YES];
NSArray *circuitDescriptors = [[NSArray alloc] initWithObjects:circuitDescriptor, nil]; 
NSArray *circuits = [thisBoard.circuits sortedArrayUsingDescriptors:circuitDescriptors];

 //int circuitCount = circuits.count;
 ICPDFCircuitDetails *circuitDetails = boardPage.circuitDetails;

int circuitCount = 0;
for (Circuit *thisCircuit in circuits) {
circuitCount++;
if (circuitCount > 16) {
    // Add an extension page
    DebugLog(@"Adding an extension sheet");
    circuitCount = 1;
    ICPDFDistributionBoardExtension *boardExtension = [[ICPDFDistributionBoardExtension  alloc]   initWithDistributionBoard:thisBoard];
    [pagesArray addObject:boardExtension];
    boardExtension.pageNumber.text = [NSString stringWithFormat:@"%d", ++pageCount];
    circuitDetails = boardExtension.circuitDetails;
   }
    NSString *key = [NSString stringWithFormat:@"circuitRow%d", circuitCount];
   ICCircuitRow *circuitRow = [circuitDetails valueForKey:key];
   [circuitRow populateFromCircuit:thisCircuit];
  }
 }
}

调试控制台警告是一个负载其中,然后崩溃

debug console warnings are a load of these, then crash

 2013-02-08 10:38:35.475 iCertifi[5772:907] Received memory warning.
 2013-02-08 10:38:35.477 iCertifi[5772:907] <ICPDFPreviewController: 0x1eb28930>   didReceiveMemoryWarning


推荐答案

很久以前在一个完全不同的平台上我正在生成PDF文件并遇到这种问题。解决方案是一次生成一个页面(关闭每个页面(不是文档),然后打开下一个页面进行成像)。这对我的许多页面文档产生了巨大的影响。查看您的代码,这与您在此处遇到的问题相同。实际上,您还有一个问题,那就是您首先以图形方式在内存中构建所有页面数据,然后在内存中构建所有pdf页面并使其崩溃。请阅读以下有关如何解决问题的详细信息。

A long time ago on a completely different platform I was generating PDF files and running into this kind of problem. The solution there was to generate a single page at a time (closing each page (not the document) before opening the next one for imaging). This made a huge difference for my many page documents. Looking at your code, this is the same issue you are facing here. Actually, you have an additional issue which is that you are building all the page data in memory graphically first and then building all the pdf pages also all in memory and it crashes. Read below for details on how to fix the problem.

阅读这里

看来您想要的序列是:

// start pdf document using default page size (CGRectZero)
UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil); 

// then loop through your content drawing it one page at a time.
for ( page p in pages )   // or whatever you are cycling over for page content
{
   UIGraphicsBeginPDFPage();   // or UIGraphicsBeginPDFPageWithInfo

  // do your drawing to the page here by drawing to the current graphics context.
  // if you are setting up transforms and such

}
// close out the last page and the document both
UIGraphicsEndPDFContext();

那么这应该做的是一次将内存中的渲染数据量保持为一页(加上文档全局数据的一些开销。)

So what this should do is keep the amount of rendered data in memory to one page at a time (plus some overhead for the document global data).

好的。我查看了你的代码,如果我理解正确的话,看起来你最终会在内存中找到至少2份渲染页面。

Ok. I looked over your code and it looks like you are ending up with at least 2 copies of the rendered page in memory, if I'm understanding it correctly.

这是键 - 一次只能绘制一页。你没有显示所有 ICPDFEICRPage1 等等,但看起来它们是将页面内容呈现给某种UIView的对象?这意味着他们拥有整个视图的像素数据(即副本1)。因此,在为任何pdf页面呈现之前,您将在每个页面上使用内存。然后打开PDF页面上下文,将其呈现为 NSMutableData (这是数据的第二个副本)。

Here's the key - only draw one page at a time. You don't show what all the ICPDFEICRPage1 and such are, but it appears that they are objects that are rendering page content to a UIView of some kind? That means they have the pixel data for an entire view (that's copy 1). So you are using memory for every single page you will draw all at once before you even render any pdf pages. Then you open the PDF page context that renders it to an NSMutableData (that's the second copy of the data).

另一个问题是:为什么要渲染某种内存构造(视图),而不是在打印该页面时只渲染到每页的pdf页面图形上下文?我猜测这是因为你有一个UIView层次结构,其中包含字段和xib文件中定义的所有内容,因此您不必手动完成所有字符串的布局和绘制。正确?

The other issue is: why are you rendering to some kind of in memory construct (a view) instead of rendering just onto the pdf page graphics context for each page as you print that page? I'm guessing that this is because you have a UIView hierarchy with fields and everything defined in an xib file so that you don't have to do all the layout and drawing of strings and such manually. Correct?

如果是这样,那么要做的就是让你的pages数组成为一个数组,列出 ICPDFEICR中的哪一个... 您想要的对象是您想要的对象,而不是实际分配的对象本身。也许为每种可能类型的页面,观察,分发板等定义枚举。然后你的pages数组只是一个整数数组(那些枚举按你想要在文档中显示的顺序)。

If so, then the thing to do is to have your pages array be an array be some kind of list of which of the ICPDFEICR... objects you want in what order you want them, but not the actual allocated objects themselves. Perhaps define an enum for each possible type of page, observations, distribution boards, and such. Then your pages array is just an array of integers (those enums in the order you want to display them in the document).

然后只为当前分配实际对象页面,将其映像到页面,然后在下一页之前将其释放。

Then only allocate the actual object for the current page, image it to the page, and then release it before you do the next page.

另外,我假设(希望!)你使用ARC,否则你有这段代码中有很多内存泄漏(!!!)。

Also, I assume (hope!) you are using ARC because otherwise you have a lot of memory leaks in this code (!!!).

这有望让你开始走上正轨。哦,使用文件技术,你需要给它一个文件路径,然后你只需将该路径作为file:// url传递给webview进行显示。

That will hopefully be enough to get you started on the right track. Oh, with the file technique you'll need to give it a file path and then you just pass that path as a file:// url to the webview to display.

虽然现在我想到了,为什么你使用UIWebView来显示PDF?查看缩放PDF查看器示例以获取替代方案不会尝试将整个PDF文档加载到内存中以显示它。

Though now that I think of it, why you are using a UIWebView to display PDF? Look at the Zooming PDF Viewer sample for an alternative that doesn't try to load the whole PDF document into memory to display it.

这篇关于创建PDF时内存警告和崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆