0
votes

I have an app that generates PDF files. These PDf's can be large due to the user being able to add pages, potentially unlimited, although usual is approx. ten. Im having problems with iPhone 4 users experiencing a crash at the PDF genration stage. Some investigative work shows the app is running out of memory. I can reproduce the problem on If I add 50 plus pages on iPhone 5, a lot less on iPhone 4 etc and not on the simulator which is expected.

Just to clarify my app runs out of memory when generating the PDF and crashes.

Ive researched here iPhone App Crashes due to Low Memory but works fine in simulator

and here Quartz PDF API Causing Out of Memory Crashes

Is there a way I can reduce this memory usage. Ive ran it through instruments and here is what I get, Im not an experienced programmer so not sure how to interpret this properly or fix this.

enter image description here

- (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);
    //
   // CGContextSetInterpolationQuality((__bridge CGContextRef)(thisPage), kCGInterpolationHigh); CGContextSetRenderingIntent((__bridge CGContextRef)(thisPage), kCGRenderingIntentDefault);
   //

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

- (void)addObservationsToPagesArray:(NSMutableArray *)pagesArray
{
int pageCount = pagesArray.count;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdAt" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; 
NSArray *observations = [self.certificate.observations sortedArrayUsingDescriptors:sortDescriptors];

ICPDFObservations *observationsPage = [[ICPDFObservations alloc] initWithCertificate:self.certificate];
if (observations.count > 28) {
    observationsPage.additionalObservations.text = @"\u2714";
    observationsPage.additionalNotesAttachedOrToFollowRef.text = @"Attached";
}
observationsPage.pageNumber.text = [NSString stringWithFormat:@"%d", ++pageCount];
DebugLog(@"Page number is %d", pageCount);
[pagesArray addObject:observationsPage];

ICObservationTable *observationTable = observationsPage.observationTable;

int observationCount = 0;
for (Observation *observation in observations) {
    observationCount++;
    if (observationCount > 28) {
        // Add an extension page
        DebugLog(@"Adding an extension sheet");
        observationCount = 1;
        ICPDFObservationsExtension *observationsExtension = [[ICPDFObservationsExtension alloc] initWithCertificate:self.certificate];
        [pagesArray addObject:observationsExtension];
        observationTable = observationsExtension.observationTable;
    }
    NSString *key = [NSString stringWithFormat:@"observationRow%d", observationCount];
    ICObservationRow *observationRow = [observationTable valueForKey:key];
    [observationRow populateFromObservation:observation];
  }

 }
1

1 Answers

0
votes

Use instruments to check for leaks and memory loss due to retained but not leaked memory. The latter is unused memory that is still pointed to. Use Heapshot in the Allocations instrument on Instruments.

For HowTo use Heapshot to find memory creap, see: bbum blog

Basically there method is to run Instruments allocate tool, take a heapshot, run an intuition of your code and another heapshot repeating 3 or 4 times. This will indicate memory that is allocated and not released during the iterations.

To figure out the results disclose to see the individual allocations.

If you need to see where retains, releases and autoreleases occur for an object use instruments:

Run in instruments, in Allocations set "Record reference counts" on on (you have to stop recording to set the option). Cause the picker to run, stop recording, search for there ivar (datePickerView), drill down and you will be able to see where all retains, releases and autoreleases occurred.