5

I have two instances of NSManagedObjectContext: one is used in main thread and the other is used in a background thread (via an NSOperation.) For thread safety, these two contexts only share an NSPersistentStoreCoordinator.

The problem I'm having is with pending changes in the first context (on the main thread) not being available to the second context until a -save is performed. This is understandable since the shared persistent store won't have copies of the NSManagedObjects being tracked by -insertedObjects, -updatedObjects, and -deletedObjects are persisted.

Unfortunately, this presents a problem with the user experience: any unsaved changes won't appear in the (time consuming) reports that are generated in the background thread.

The only solution I can think of is nasty: take the inserted, updated and deleted objects from the first context and graft them onto the object graph of the second context. There are some pretty complex relations in the dataset, so I'm hesitant to go in this direction. I'm hoping someone here as a better solution.

chockenberry
  • 7,811
  • 5
  • 32
  • 41

4 Answers4

4

If this is under 10.7 there are some solutions: one is you can have nested ManagedObjectContexts, so you can “save” in the one being modified and it won’t save all the way to the disk, but it will make the changes available to other children of the master context.

Before 10.7 you will probably have to copy the changes over yourself. This isn’t super-hard since you can just have a single object listen for NSManagedObjectContextObjectsDidChangeNotification and then just re-apply the changes exactly from the main context. (Should be about 20 lines of code.) You never have to save this second context I assume?

Wil Shipley
  • 9,343
  • 35
  • 59
  • Thanks Wil! I'd like to target 10.6, although that's getting harder every day :-) -- I'm already tracking the change notification, but I'm not sure how you re-apply the changes and maintain the entity relationships. Can you be a little more specific? – chockenberry Mar 15 '12 at 22:05
  • Ah yes. I was invisibly making an assumption based on my model, which is that every object has a UUID (string) key that you maintained yourself. – Wil Shipley Mar 15 '12 at 22:31
  • Without the save in the background MOC the inserts will not be visible to the main MOC (without nested MOCs). Updates/deletes for existing objects should work if you listen to the change notification (issued after processPendingChanges) and update the objects yourself. – diederikh Mar 16 '12 at 09:31
2

Not sure if you have any OS restraints but in iOS 5 / Mac OS 10.7 you can use nested managed object contexts to accomplish this. I believe a child context is able to pull in unsaved changes in the parent by simply doing a new fetch.

Edit: Looks like Wil beat me to it but yeah, prior to iOS 5 / Mac OS 10.7 you'll have to listen for the NSManagedObjectContextDidSaveNotification and take a look at the userInfo dictionary for the added/updated/deleted objects.

0

An alternate solution might involve using a single managed object context and providing your own thread safety over access to it, or use the context's lock and unlock methods.

Graham Perks
  • 23,007
  • 8
  • 61
  • 83
0

I would try to make the main thread do a normal save so the second context can just merge the changes into his context. "fighting" a APIs intended use is never an good idea. You could mark the newly saved record with an attribute as intermediate and delete later if the user finally cancels the edit.

Solving those problems with attributes in your entities and querying in the background thread with a matching predicated would be easy...

And that would be a stable solution as well. I am coming from a database driven world (oracle) we often use such patterns (status attributes in records) to make data visible/invisible to other DB sessions (which would equal to threads in an cocoa app). Works always without problems. Other threads /sessions do always only see commited changes that's how most RDBMS work.