JSA*_*986 5 cocoa-touch memory-management objective-c ios
在生成大型PDF时,我的应用程序会在内存中收到警告,然后在PDF的生成过程中崩溃.PDF被绘制到Web视图中,当页面超过一定数量(取决于设备)时,我的内存不足
到目前为止,我对这个问题的研究使我理解我需要:
改变UIGraphicsBeginPDFContextToData以IGraphicsBeginPDFContextToFile
创建临时文件的合理路径,
把它交给函数,
将文件提供给webview以加载.
完成后删除文件.
问题是,虽然我认为我(仅仅)在市政中抓住它,但我不知道如何实现这一点,或者完全理解它以便在我的代码中实现它.这个问题的建议非常适合
我也对任何其他想法开放以阻止内存崩溃
@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];
}
}
}
Run Code Online (Sandbox Code Playgroud)
调试控制台警告是这些的负载,然后崩溃
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
Run Code Online (Sandbox Code Playgroud)
很久以前,在一个完全不同的平台上,我正在生成PDF文件并遇到这种问题.解决方案是一次生成一个页面(关闭每个页面(不是文档),然后打开下一个页面进行成像).这对我的许多页面文档产生了巨大的影响.查看您的代码,这与您在此处遇到的问题相同.实际上,您还有一个问题,就是首先以图形方式在内存中构建所有页面数据,然后在内存中构建所有pdf页面并使其崩溃.请阅读以下有关如何解决问题的详细信息.
在这里读
您想要的序列似乎是:
// 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();
Run Code Online (Sandbox Code Playgroud)
所以这应该做的是一次将内存中的渲染数据量保持为一页(加上文档全局数据的一些开销).
好.我查看了你的代码,看起来你最终在内存中至少有2个渲染页面的副本,如果我正确理解它.
这是关键 - 一次只能绘制一页.你没有显示所有ICPDFEICRPage1这些是什么,但似乎它们是将页面内容呈现给某种UIView的对象?这意味着他们拥有整个视图的像素数据(即副本1).因此,您甚至在渲染任何pdf页面之前,将为每个页面使用内存.然后打开PDF页面上下文,将其呈现为NSMutableData(这是数据的第二个副本).
另一个问题是:为什么要渲染某种内存构造(视图),而不是在打印该页面时只渲染到每个页面的pdf页面图形上下文?我猜这是因为你有一个UIView层次结构,其字段和一切都在xib文件中定义,这样你就不必手动完成所有的字符串布局和绘图.正确?
如果是这样,那么要做的就是让你的pages数组成为一个数组,列出ICPDFEICR...你想要的对象中你想要的对象,而不是实际分配的对象本身.也许为每种可能类型的页面,观察,分发板等定义枚举.然后你的pages数组只是一个整数数组(那些枚举按你想要在文档中显示的顺序).
然后,仅为当前页面分配实际对象,将其映像到页面,然后在执行下一页之前将其释放.
另外,我假设(希望!)你正在使用ARC,否则你在这段代码中会有很多内存泄漏(!!!).
希望这足以让你开始走上正轨.哦,使用文件技术,你需要给它一个文件路径,然后你只需将该路径作为file:// url传递给webview进行显示.
虽然现在我想到了,为什么你使用UIWebView来显示PDF?查看缩放PDF查看器示例,以获取不尝试将整个PDF文档加载到内存中以显示它的替代方法.