1

I have a a main NSManagedObjectContext used in a few UIViewControllers to display the data (which is a UITableView with a list of Department)

3 entities one Department with a to-one to Boss with a to-many to Employee (In this case the employee have an NSData (which is an image) attribute with allow external storage).

Since I'm importing images in batches I'm doing it in a background thread which has its own NSManagedObjectContext.

The importing consists in creating the Boss entity and all the Employee and setting up the relationships.

Now my issue is :

  • if I use a child context of the main context for importing and save, then all the images stay in memory even though both context don't have changes.
  • if I use a context with no relation to the main context the image aren't staying in memory but the new data isn't showed in the UIViewController (obviously since the main context isn't notified of the changes done by the background context)

So I would like to still have the changes appear without having the images in memory (meaning I would like the Department to know that it has a Boss relationship but without having the images in memory). In short I would like them to be turned into fault as soon as the context is saved.

EDIT : I think the problem is when I save the child context, it merges with the main context and from there the newly inserted images stay in memory :/ and I have no idea how to release them (and no they're not auto released even with memory warning...)

EDIT 2 : I think I fixed it, here's what I did :

  • I used a child context tied to the main context and I listened to all the NSManagedObjectContextDidSaveNotification and for all the inserted updated I call refreshObject:mergeChanges: on it to turn it into fault.

I registered for all the notifications from every context.

-(void)contextDidSave:(NSNotification*)saveNotification {
    NSManagedObjectContext *defaultContext = saveNotification.object;
    NSArray *insertedObjects = [saveNotification.userInfo valueForKey:@"inserted"];
    if (insertedObject) {
        NSLog(@"INSERTED : %@", insertedObjects);
        for (NSManagedObject *object in insertedObjects) {
            [defaultContext refreshObject:object mergeChanges:NO];
        }
    }
    NSArray *updatedObjects = [saveNotification.userInfo valueForKey:@"updated"];
    if (insertedObject) {
        NSLog(@"UPDATED : %@", updatedObjects);
        for (NSManagedObject *object in updatedObjects) {
            [defaultContext refreshObject:object mergeChanges:NO];
        }
    }
}
ItsASecret
  • 2,589
  • 3
  • 19
  • 32
  • This sounds like premature optimisation; the MOC should decide when to fault things for you. Are you actually having a problem caused by this, or do you just think it ought to work in a certain way? – jrturton Mar 03 '13 at 11:15
  • Yes I am after 5-7 batch import (about 28Mb of images data /import) the app crashes, because the images are never faulted back. – ItsASecret Mar 03 '13 at 11:44
  • Seems a bit messy having to have the notifications - the child context is supposed to solve that for you. Can't you call refreshObject on the child context, after you've finished adding and saving each one? – jrturton Mar 03 '13 at 14:44
  • I tried but I think the objects are still living in the parent context – ItsASecret Mar 03 '13 at 15:14

1 Answers1

1

You can turn a specific object into a fault using refreshObject:mergeChanges:, passing NO for the mergeChanges argument.

Turning object into a fault (flag is NO) means that strong references to related managed objects (that is, those to which object has a reference) are broken, so you can also use this method to trim a portion of your object graph you want to constrain memory usage.

Documentation here.

jrturton
  • 118,105
  • 32
  • 252
  • 268
  • Yeah I know that I read it like everywhere this seems to be the only way but I tried calling it and it doesn't seem to work in this case (it works when I call it on the main context but not the child) Also I read all the core data documentation I could find :/ – ItsASecret Mar 03 '13 at 11:44
  • Okay so I think I found a way to do what I wanted I'm gonna mark your answer as valid but before that could you tell me what you think about the way I solved it ? If it seems decent :/ ? – ItsASecret Mar 03 '13 at 14:10
  • If you solved it yourself, you should answer your own question, and accept that answer, rather than edit the question. – jrturton Mar 03 '13 at 14:41