Reputation: 826
My understanding from the Core Data Programming Guide is that when a context is saved, for each managed object of that context the Core Data framework compares a snapshot of its values taken when the object was last fetched against the now-current corresponding values in the persistent store. With the default NSErrorMergePolicy
, an attempt to save the context will throw an error if the version numbers associated with these snapshots are different.
However, I am observing that given a background context that has made changes, the context gets a save conflict even if in the meantime another context (the main UI context) has also made changes to one of its managed objects but has not made a call to save those changes to the persistent store.
My question is: while I recognize the obviousness of having writes to both contexts concurrently - how is the background context able to trigger a save conflict when this context is the first to save? The main context has merely written to one of its managed objects - with at most a possible call to processPendingChanges
- but no save.
Other details
I should note both contexts in the example above share the same persistent store coordinator. I am wondering if there is some communication at this level I'm not considering. However, I thought that the point is that contexts are 'isolated scratch-pads' that act independently until a call to save commits/merges the changes to the store as a transaction. I also recognise I can solve the problem by changing the merge policy, but it's the lack of that isolation that concerns me. I am also observing objects-did-change and context-did-save notifications on both contexts so I don't think there is a spurious save that I'm not aware of. But what else have I missed?
Finally, It's hard to show the code as the problem is inherently structure / timing related. But any other suggestions to discover exactly when the UI context is touching the PSC/store to increment the version numbering - would be much appreciated, too.
Upvotes: 3
Views: 857
Reputation: 826
I found the problem, which might be a useful gotcha should anyone else observe similar behaviour.
I believe my assumptions in the question are indeed correct, and on a first pass it may not be immediately obvious that everything runs as expected. However, on a later pass, in my case it was possible that the main UI has made a call to save its context by now, while managed objects in the background context remain at the same state since their first (successful) call to save. It was this second call to save the background context that threw the save conflict.
An immediate solution to confirm such a problem is to call refreshAllObjects()
or refresh(_:mergeChanges:)
somewhere near the start a processing block that may have stale objects. This will make sure managed objects in the context are updated with the latest changes from the persistent store, and so provided no saves occur during this block a later call to save should see no conflicts.
I should mention that I don't wish to say this is a good approach to such problem - I just wanted to understand what was happening.
Upvotes: 3