5

大きなPDFを生成するとき、私のアプリはメモリに警告を受け取り、PDFの生成プロセス中にクラッシュします。PDFがWebビューに描画され、ページが特定の量を超えると(デバイスによって異なります)、メモリが不足します

これまでの私の研究は、私が次のことをする必要があることを理解するように導きます。

UIGraphicsBeginPDFContextToDataに変更IGraphicsBeginPDFContextToFile

  1. 一時ファイルへの適切なパスを作成し、

  2. それを関数に与えて、

  3. ファイルをWebビューに渡してロードします。

  4. 完了したらファイルを削除します。

問題は、私はそれを(ちょうど)原則的に把握していると思いますが、これを達成する方法がわからない、またはコード内でそれを暗示するように完全に理解していることです。この件に関するアドバイスは非常に適用されます

私はまた、メモリクラッシュを止めるために他のアイデアを受け入れます

@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];
  }
 }
}

デバッグコンソールの警告はこれらの負荷であり、その後クラッシュします

 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
4

4 に答える 4

5

昔、まったく別のプラットフォームで PDF ファイルを生成していて、この種の問題に遭遇しました。そこでの解決策は、一度に 1 つのページを生成することでした (イメージングのために次のページを開く前に、(ドキュメントではなく) 各ページを閉じます)。これは、私の多くのページ ドキュメントに大きな違いをもたらしました。コードを見ると、これはここで直面している問題と同じです。実際には、最初にメモリ内のすべてのページ データをグラフィカルに構築し、次にすべての 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();

したがって、これが行うべきことは、メモリ内のレンダリングされるデータの量を一度に 1 ページに保つことです (さらに、ドキュメントのグローバル データのオーバーヘッドがいくらかかかります)。

Ok。コードを調べたところ、レンダリングされたページの少なくとも 2 つのコピーがメモリ内にあるようです。正しく理解していれば。

重要なのは、一度に 1 ページだけを描くことです。すべてが何であるかは示されていませんICPDFEICRPage1が、それらはページコンテンツを何らかの UIView にレンダリングしているオブジェクトであるように見えますか? つまり、ビュー全体 (コピー 1) のピクセル データがあることを意味します。したがって、PDFページをレンダリングする前に、一度に描画するすべてのページにメモリを使用しています。次に、それをレンダリングする PDF ページ コンテキストを開きますNSMutableData(これがデータの 2 番目のコピーです)。

もう1つの問題は、ページを印刷するときに各ページのpdfページのグラフィックスコンテキストだけにレンダリングするのではなく、メモリ内の構造(ビュー)にレンダリングするのはなぜですか? これは、フィールドとすべてがxibファイルで定義されたUIView階層があり、すべてのレイアウトと文字列の描画などを手動で行う必要がないためだと思います。正しい?

その場合、ページ配列を配列にしてICPDFEICR...、実際に割り当てられたオブジェクト自体ではなく、必要なオブジェクトをどの順序で並べるかのリストのようなものにする必要があります。おそらく、考えられるページ、観察、配布ボードなどの種類ごとに列挙型を定義します。次に、ページ配列は単なる整数の配列です(ドキュメントに表示する順序の列挙型)。

次に、現在のページの実際のオブジェクトのみを割り当て、それをページにイメージしてから、次のページを実行する前に解放します。

また、ARC を使用していると思います (願っています!)。そうしないと、このコードで大量のメモリ リークが発生します (!!!)。

うまくいけば、それはあなたが正しい道を歩み始めるのに十分でしょう. ああ、ファイル テクニックでは、ファイル パスを指定する必要があります。次に、そのパスを file:// url として Web ビューに渡して表示します。

そういえば、なぜ UIWebView を使って PDF を表示しているのですか?PDF ドキュメント全体をメモリにロードして表示しようとしない別の方法については、Zooming PDF Viewerサンプルを参照してください。

于 2013-02-08T05:19:08.710 に答える
2

これは問題を解決するのに役立つかもしれません。コードに含まれていることに注意してください

  • ファイルの作成
  • コンテンツを追加する[afterUIGraphicsGetCurrentContext();メソッドで描画を開始する
  • WebView での表示

ボタンアクションからスタート

- (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];
}
于 2013-02-08T06:11:38.433 に答える