2013-02-05 46 views
5

Khi tạo một tệp PDF lớn, ứng dụng của tôi lưu lại một cảnh báo bộ nhớ, sau đó treo trong quá trình tạo PDF. PDF được hút vào một cái nhìn web, khi các trang được trên một số tiền nhất định (tùy theo thiết bị) Tôi chạy ra khỏi bộ nhớCảnh báo bộ nhớ và sự cố khi tạo PDF

Nghiên cứu của tôi về vấn đề này cho đến nay dẫn tôi hiểu tôi cần phải:

thay đổi UIGraphicsBeginPDFContextToData để IGraphicsBeginPDFContextToFile

  1. Tạo một con đường hợp lý vào một tập tin tạm thời,

  2. cho rằng đến chức năng,

  3. Đặt tệp cho chế độ xem web để tải.

  4. Xóa tệp khi hoàn tất.

Vấn đề là, trong khi tôi nghĩ rằng tôi nắm bắt được điều này, tôi không biết làm thế nào để đạt được điều này, hoặc hiểu đầy đủ nó để cấy ghép nó trong mã của tôi. Lời khuyên trong vấn đề này rất nhiều appricated

Im cũng mở cửa cho bất kỳ ý tưởng khác để ngăn chặn sự sụp đổ của bộ nhớ

@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 cảnh báo là một tải trong số này, sau đó sụp đổ

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 
+0

Bao gồm crashlog cũng –

+0

Tôi xin lỗi nhưng tôi nghĩ rằng tôi đã bỏ lỡ một cái gì đó. Bạn muốn tạo một tập tin PDF từ những gì? Nguồn của bạn là gì? Một số bối cảnh đồ họa? Và nơi nào bạn có vấn đề bộ nhớ? Trong khi tạo tệp hoặc hiển thị tệp trong chế độ xem web? –

Trả lời

5

Một dài thời gian trước đây trên một nền tảng hoàn toàn khác nhau, tôi đã tạo ra các tập tin PDF và chạy vào loại vấn đề. Giải pháp có để tạo ra một trang duy nhất tại một thời điểm (đóng mỗi trang (không phải tài liệu) trước khi mở trang tiếp theo để chụp ảnh). Điều này tạo ra sự khác biệt lớn cho nhiều tài liệu trang của tôi. Nhìn vào mã của bạn, đây là vấn đề tương tự bạn đang gặp phải ở đây. Trên thực tế, bạn có một vấn đề khác đó là bạn đang xây dựng tất cả các dữ liệu trang trong bộ nhớ đồ họa đầu tiên và sau đó xây dựng tất cả các trang pdf cũng tất cả trong bộ nhớ và nó bị treo. Đọc bên dưới để biết chi tiết về cách khắc phục sự cố.

Reading here

Dường như chuỗi bạn muốn là:

// 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(); 

Vì vậy, điều này nên làm là giữ số lượng dữ liệu kết xuất trong bộ nhớ để một trang tại một thời điểm (cộng với một số overhead cho dữ liệu toàn cầu của tài liệu).

Ok. Tôi đã xem xét mã của bạn và có vẻ như bạn đang kết thúc với ít nhất 2 bản sao của trang được hiển thị trong bộ nhớ, nếu tôi hiểu nó chính xác.

Đây là khóa - chỉ vẽ một trang tại một thời điểm. Bạn không hiển thị tất cả các số ICPDFEICRPage1 và những thứ như vậy, nhưng dường như chúng là các đối tượng đang hiển thị nội dung trang cho UIView thuộc loại nào đó? Điều đó có nghĩa là chúng có dữ liệu pixel cho toàn bộ chế độ xem (đó là bản sao 1). Vì vậy, bạn đang sử dụng bộ nhớ cho mỗi trang duy nhất bạn sẽ vẽ tất cả cùng một lúc trước khi bạn thậm chí làm cho bất kỳ trang pdf. Sau đó, bạn mở ngữ cảnh trang PDF để hiển thị nó thành một NSMutableData (đó là bản sao thứ hai của dữ liệu).

Vấn đề khác là: tại sao bạn hiển thị một số loại trong cấu trúc bộ nhớ (xem) thay vì chỉ hiển thị trên ngữ cảnh đồ họa trang pdf cho mỗi trang khi bạn in trang đó? Tôi là đoán rằng điều này là do bạn có phân cấp UIView với các trường và mọi thứ được xác định trong tệp xib để bạn không phải thực hiện tất cả bố cục và bản vẽ của chuỗi và theo cách thủ công. Chính xác?

Nếu vậy, điều cần làm là để mảng trang của bạn là một mảng là danh sách các đối tượng trong số các đối tượng mà bạn muốn, chứ không phải chính các đối tượng được phân bổ thực tế. Có lẽ xác định một enum cho từng loại có thể của trang, quan sát, bảng phân phối, và như vậy. Sau đó mảng trang của bạn chỉ là một mảng các số nguyên (những enums đó theo thứ tự bạn muốn hiển thị chúng trong tài liệu).

Sau đó, chỉ phân bổ đối tượng thực tế cho trang hiện tại, hình ảnh đó đến trang và sau đó phát hành nó trước khi bạn làm trang tiếp theo.

Ngoài ra, tôi giả sử (hy vọng!) Bạn đang sử dụng ARC bởi vì nếu không bạn có rất nhiều rò rỉ bộ nhớ trong mã này (!!!).

Điều đó hy vọng sẽ đủ để bạn bắt đầu đi đúng hướng. Oh, với kỹ thuật tập tin bạn sẽ cần phải cung cấp cho nó một đường dẫn tập tin và sau đó bạn chỉ cần vượt qua con đường đó như là một tập tin: // url để webview để hiển thị.

Mặc dù bây giờ tôi nghĩ về nó, tại sao bạn đang sử dụng UIWebView để hiển thị PDF? Nhìn vào mẫu Zooming PDF Viewer cho một giải pháp thay thế không cố gắng tải toàn bộ tài liệu PDF vào bộ nhớ để hiển thị nó.

+0

Ive nhìn vào điều này trên một tìm kiếm của Google và không chắc chắn làm thế nào để tiếp cận này liên quan đến mã của tôi, bất kỳ lời khuyên? – JSA986

+0

Tôi mở rộng câu trả lời của tôi rất nhiều với rất nhiều thông tin cụ thể hơn vì vậy nếu bạn sẽ đọc nó một lần nữa tôi tin rằng bạn sẽ tìm thấy nó trả lời câu hỏi của bạn. – Dad

+0

Chỉ muốn nói lời cảm ơn vì câu trả lời rất chi tiết, Im vẫn đang cố gắng khắc phục vấn đề này, tôi không thể có được đầu của tôi xung quanh nó mặc dù đọc lại bạn trả lời nhiều lần. Có im sử dụng ARC, Cảm ơn một lần nữa – JSA986

2

Có thể điều này có thể giúp bạn giải quyết vấn đề. Lưu ý rằng mã chứa

  • tạo ra một tập tin
  • thêm các nội dung [bắt đầu vẽ ở sau UIGraphicsGetCurrentContext(); phương pháp
  • xem trong một webview

Bắt đầu từ tha button action

- (IBAction)buttonPress:(id)sender 
{ 
    [self createPDFData:[self getPDFFileName]]; 
} 
-(NSString *)getPDFFileName 
{ 
    NSString * fileName = @"Info.PDF"; 

    NSArray *arrayPaths = 
    NSSearchPathForDirectoriesInDomains(
             NSDocumentDirectory, 
             NSUserDomainMask, 
             YES); 
    NSString * path = [arrayPaths objectAtIndex:0]; 
    NSString *pdfFileName = [path stringByAppendingPathComponent:fileName]; 
    NSLog(@"path : %@",path); 
    NSLog(@"pdf file name : %@",pdfFileName); 
    return pdfFileName; 

} 

-(void)showPDFFile 
{ 


    UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0,44,1024,748)]; 

    NSURL *url = [NSURL fileURLWithPath:[self getPDFFileName]]; 
    NSURLRequest *request = [NSURLRequest requestWithURL:url]; 
    [webView setScalesPageToFit:YES]; 
    [webView loadRequest:request]; 

    [self.view addSubview:webView]; 

} 


-(void)createPDFData:(NSString *)fileName{ 

    CGRect pageFrame = CGRectMake(0,0,768,1024); 
    UIGraphicsBeginPDFContextToFile(fileName, CGRectZero, nil);//This starts drawing a pdf to file named "filename" UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);//For each new page call this 
    UIGraphicsGetCurrentContext(); 
    UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);//Next or new Page 
    NSString * timeLabel = [NSString stringWithFormat:@"Some text"]; 
    [timeLabel drawInRect:CGRectMake(200, 200, 428, 44) withFont:[UIFont boldSystemFontOfSize:10] lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft]; 
    UIGraphicsEndPDFContext(); 
    [self showPDFFile]; 
} 

Mã hóa hạnh phúc :)

0

Cố gắng sử dụng @autoreleasepool tại của bạn cho chu kỳ

for (int i = 0; i < pages.count; i++) { 

// notice this line 
    @autureleasepool 
    { 
     ICCertificateComponent *thisPage = [pages objectAtIndex:i]; 
     UIGraphicsBeginPDFPageWithInfo(thisPage.contentView.bounds, nil); 


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

Nếu không có gì khác hoạt động bạn có thể thử giải pháp của tôi. Nó cuộn qua webView và thêm một trang cùng một lúc vào tệp PDF. Tôi không thể giải quyết vấn đề này bằng bất kỳ cách nào khác. Mặt sau của giải pháp này là nó sử dụng bộ hẹn giờ và bạn sẽ thấy cuộn trong ứng dụng trong khi PDF được tạo.

Dưới đây là các mã:

... 
    CGPoint savedContentOffset; 
    CGRect savedFrame; 
    NSMutableData * pdfData; 
    int pdfOffset; 
    NSTimer *timer; 
... 

- (void)preparePdf { 
    CGFloat height = aWebView.scrollView.contentSize.height; 
    CGRect screenRect = [[UIScreen mainScreen] bounds]; 
    int pageHeight = screenRect.size.height; 

    savedContentOffset = aWebView.scrollView.contentOffset; 
    savedFrame = aWebView.frame; 
    pdfData=[NSMutableData data]; 

    aWebView.scrollView.contentOffset = CGPointZero; 
    aWebView.frame = CGRectMake(aWebView.frame.origin.x,aWebView.frame.origin.y,aWebView.frame.size.width,pageHeight); 

    UIGraphicsBeginPDFContextToData(pdfData, aWebView.frame, nil); 

    NSLog(@"pageHeight = %d", pageHeight); 
    pdfOffset = 0; 

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.4 
              target: self 
              selector: @selector(printPdfPage:) 
              userInfo: nil 
              repeats: YES]; 
} 

- (void) printPdfPage: (NSTimer*) timer { 
    CGFloat height = aWebView.scrollView.contentSize.height; 
    CGRect screenRect = [[UIScreen mainScreen] bounds]; 
    int pageHeight = screenRect.size.height; 

    NSLog(@"pdfOffset = %d", pdfOffset); 
    UIGraphicsBeginPDFPage(); 
    [aWebView drawViewHierarchyInRect:CGRectMake(0, 0, screenRect.size.width, pageHeight) afterScreenUpdates:YES]; 

    pdfOffset += pageHeight; 
    aWebView.scrollView.contentOffset = CGPointMake(0, pdfOffset); 

    if (pdfOffset > height) { 
     [timer invalidate]; 
     NSLog(@"pdf data size: %ld", pdfData.length); 
     [self emailPdf: pdfData]; 
    } 
} 

- (void) emailPdf: (NSMutableData*) pdfData { 

    // finally end the PDF context. 
    UIGraphicsEndPDFContext(); 

    aWebView.scrollView.contentOffset = savedContentOffset; 
    aWebView.frame = savedFrame; 

    MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init]; 
    mailer.mailComposeDelegate = self; 
    [mailer setSubject:[@"iPad Screenshot " stringByAppendingString:[self getDate]]]; 
    [mailer addAttachmentData:pdfData mimeType:@"application/pdf" fileName:[[self getDate] stringByAppendingPathExtension:@"pdf"]]; 
    NSString *emailBody = @""; 
    [mailer setMessageBody:emailBody isHTML:NO]; 
    [self presentViewController:mailer animated:YES completion:nil]; 

    if ([SVProgressHUD isVisible]) { 
     [SVProgressHUD showSuccessWithStatus:@"Screenshot prepared."]; 
    } 
    pdfData = nil; 
    timer = nil; 
} 
Các vấn đề liên quan