Reputation: 12499
I have an NSMutableArray
property in a class where I keep reference to some managed objects of an NSManagedObjectContext
which I called mainContext
and that is associated to main queue. In this same class, I create a privateContext
in a private queue and I set it as a child of the mainContext
.
I'd like to pass the objects in my NSMutableArray
(self.entities
), which belong to the mainContext
, to its child privateContext
and, at the same time, keep a reference of such objects once they are in the privateContext
in another array (self.tempEntities
). I want to keep these references because I'll insert new objects later in privateContext
and I'd need to easily know which of the objects that are at that moment in privateContext
came from its parent mainContext
.
I'm not sure if this way of doing this is correct:
for (MyEntity *entity in self.entities) { // this is main thread
[self.privateContext performBlockAndWait: ^{
[self.privateContext objectWithID:entity.objectID];
[self.tempEntities addObject:entity];
}];
}
Or this will cause any problem later. Or maybe is there another and better way to do this?
Thanks
EDIT: How will be the parent context updated in this case when the child is saved? I mean: the purpose of passing to the child context the objects in the parent, in my scenario, is that I need to compare in the child context its new objects with the ones that were passed from the parent. Then, maybe I need to delete some objects that came from the parent, and/or replace some of the objects that came from the parent with some of the news in child, and/or insert some of the new objects in child into the parent.
Would calling [self.privateContext save:nil];
replace all objects in parent context with the objects in the child, or how is the merge handled?
Thanks again
Upvotes: 0
Views: 755
Reputation: 80265
You are still accessing the main thread object in a background thread, which is not allowed. This could work (if it does not trigger an automatic fetch), but it is safer to get the object ID before entering the background thread.
NSManagedObjectID objectID = entity.objectID;
[self.privateContext performBlockAndWait: ^{
MyEntity *bgEntity = [self.privateContext objectWithID:objectID]
// do something with the entity, e.g. update it and save.
}];
Make sure you do everything you need to do in the background in the block. I would advise not to assign these objects to the controller (as you seem to be doing with self.tempEntities
) where they are again available to the main thread. If they are accessed on the main thread, your app will crash.
Another improvement would perhaps be to use just one context.
NSArray *objectIDs = [self.entities valueForKeyPath:@"objectID"];
[self.privateContext performBlockAndWait ^{
for (NSManagedObjectID *objectID in objectIDs) {
MyEntity *bgEntity = [self.privateContext objectWithID:objectID];
// process...
}
}];
As for updating the main context: when you save a child context, all it does is "push up" the changes to the parent context. The parent context is now aware of the changes, but of course it will not automatically update your arrays.
There two avenues: the more complicated and risky one is to listen to the NSManagedObjectContextObjectsDidChangeNotification
. In the notification handler, you could update your array and the table view. Still, this is risky because there is no memory or performance optimization etc.
Better: use a NSFetchedResultsController
and implement the NSFetchedResultsControllerDelegate
methods to update your table or other data structures. You get lots of great functionality for free.
Upvotes: 4