开发者

Core Data - memory usage: my app crashes

开发者 https://www.devze.com 2023-03-05 22:23 出处:网络
My question is about core data and memory usage. I have used core data before, but this time the amount of data is higher and this made me realise that there was much more to know.I have seen that the

My question is about core data and memory usage. I have used core data before, but this time the amount of data is higher and this made me realise that there was much more to know. I have seen that there are several other similar posts and I got interesting information from them, but after applying it my apps still crashes. I have been dealing with this issue for a week now. Somebody please help.

Basically I have three subsequent similar loops of 64, 15, and 17 iterations respectively. They work fine on simulator. Tested on a couple of iPads they get memory warnings and they crash at the same iteration (number 34 of the first loop). Tested on iPad 2 it will crash at number 14 of the second loop. Instruments shows a memory usage of about 1.5 MB both live and overall. There are leaks for a few KB.

The loops perform the following (code below)

  • Execute a fetch with core data
  • For every record take a parameter stored as a row property attribute (String)
  • Call a procedure which takes that parameter and which returns data (about hundreds of KB)
  • Store these data in another property attribute (Transformable) of the same row

Pretty common task isn't it?

Now, si开发者_Go百科nce I got into memory issues, I tried to use the all the known (by me) tools at my disposal, which are:

  • release owned objects asap
  • create autorelease pools and drain them asap for not owned objects
  • save context asap
  • turn objects into faults asap

After applying all these techniques I got an exciting result: the app crashes at the exactly same point as before.

Here it is the code.

- (void) myMainProcedure {
    [self performLoop1];
    [self performLoop2];  // Similar to loop1
    [self performLoop3];  // Similar to loop1
}
- (void) performLoop1 {
    NSError * error = nil;
    NSAutoreleasePool * myOuterPool;
    NSAutoreleasePool * myInnerPool;

    NSManagedObjectContext * applicationContext = [[[UIApplication sharedApplication] delegate] managedObjectContext];
    [applicationContext setUndoManager:nil];


    NSEntityDescription * myEntityDescription = [NSEntityDescription entityForName:@"EntityName"
                                               inManagedObjectContext:applicationContext];
    NSFetchRequest * myFetchRequest = [[NSFetchRequest alloc] init];
    [myFetchRequest setEntity:myEntityDescription];

    NSString * column = @"columnName";
    NSPredicate * aWhereClause = [NSPredicate predicateWithFormat:
                                  @"(%K = %@)", column, [NSNumber numberWithInt:0]];
    [myFetchRequest setPredicate: aWhereClause];

    myOuterPool = [[NSAutoreleasePool alloc] init];
    NSArray * myRowsArray = [applicationContext executeFetchRequest:myFetchRequest                                                                     
                                                              error:&error];
    NSMutableArray * myRowsMutableArray = [[NSMutableArray alloc] initWithCapacity:0];
    [myRowsMutableArray addObjectsFromArray: myRowsArray];
    [myOuterPool drain];
    [myFetchRequest release];

    EntityName * myEntityRow;
    int totalNumberOfRows = [myRowsMutableArray count];

    myOuterPool = [[NSAutoreleasePool alloc] init];
    for (int i = 0; i < totalNumberOfRows; i++) {

            myInnerPool = [[NSAutoreleasePool alloc] init];
            myEntityRow = [myRowsMutableArray objectAtIndex:0];
            NSString * storedSmallAttribute = myEntityRow.smallAttribute;
            UIImageView * largeData = [self myMethodUsingParameter: smallAttribute];
            myEntityRow.largeAttribute = largeData;
            [myRowsMutableArray removeObjectAtIndex:0];

            [applicationContext save:&error];
            [applicationContext refreshObject:myEntityRow mergeChanges:NO];
            [myInnerPool drain];
            [largeData release];
    }
    [myOuterPool drain];
    [myRowsMutableArray release];
}

- (UIImageView *)  myMethodUsingParameter : (NSString *) link { 
    UIImageView * toBeReturned = nil;
    NSURL *pdfURL = [NSURL fileURLWithPath:link];
    CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
    CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
    CGRect pageRect = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
    UIGraphicsBeginImageContext(pageRect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();   
    CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
    CGContextFillRect(context,pageRect);
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, pageRect.size.height);
    CGContextScaleCTM(context, 1, - 1);
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh); 
    CGContextSetRenderingIntent(context, kCGRenderingIntentDefault);
    CGContextDrawPDFPage(context, page);
    CGContextRestoreGState(context);
    UIImage *imageToBeReturned = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CFRelease(pdf);  
    toBeReturned = [[UIImageView alloc] initWithImage:imageToBeReturned];
    UIGraphicsEndImageContext();
    return toBeReturned;
}
  • Please note that

    • The mutable array was introduced as a (apparently useless) strategy to have objects released sooner
    • Pools have been added ad part of the same strategy
    • The statement about interpolation quality was the only one to improve the situation (say, to move the crash a little bit forward)
    • Retain count for managed objects within the cycle ranges from 6 to 10 (?) I know that rc is not a valuable information but still, I made a test and I found out that I could send multiple release messages to managed objects before forcing the app to crash for this. But the point is that I am not supposed to release an object I don't own, am I? ....
    • The entity upon which the request is set has got also some bidirectional relationships with other entities, but still, is this relevant?

Thank you.


I think you should get a fresh look at your problem. There are many ways to deal with Core Data that are much simpler than your approach. Creating class files for your Core Data entities may help you. Also, when you are saving your files, you should seriously evaluate each objects necessity and whether it can be done a better way. In your case, I would offer two suggestions:


For each PDF URL,

  • a.Assign a unique identifier.
  • b.Save this unique identifier in your core data store.
  • c.Add url to a queue for a background process that creates
    your PDF (background process will
    allow the user to keep working while the PDFs are being generated. You
    could update your delegate on
    progress or create a temporary image that is replaced when the PDF is
    created.)
  • d.Save the image in your app directory (or photo library) using the unique identifier as a name.
  • e.When needed, load the image from disk into a UIImageView or
    whatever appropriate.

For each PDF URL,

  • a.Draw the PDF.
  • b.Get UIImage representation
  • c.Convert to PNG NSData (UIImagePNGRepresentation(image))
  • d.Save NSData in CoreData.
  • e.Load NSData and convert to UIImage when needed.
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号