NSFetchedResultsController misses updates on merged NSManagedObjectContext - core-data

I have two managed object contexts, A and B. These two contexts are kept in sync by merging changes from one to the other whenever they're saved (by monitoring NSManagedObjectContextDidSaveNotification). I've verified that changes to A and B are merging properly.
I have an NSFetchedResultsController monitoring updates to B. I find that certain updates to A do not result in the nsfrc firing on B. When a new managed object is created and added to A with the attributes the nsfrc is looking for (on B), the nsfrc fires after B merges changes from the save of A. However, if the managed object already exists on A (and B), and I modify the object and save A, while I can see that B is merging the changes from the save notification of A, the nsfrc monitoring B does not fire.
What am I missing here?

I had this problem and it seems that I've solved it. I don't know what I did exactly. I mean I did a couple of things and don't know, which one became the solution. So I will simply describe...
I've refactored my code to follow some "hints", found in official docs & some forums:
If you manage NSManagedObjectContext in another thread, it should be created in that thread and not somehow passed there.
You should add observer (to get save notifications for merging) in main thread only - this way they will be also merged in the main thread.
(Optional, but I did it) You can try to save context in main thread only even if it was created and managed in the background thread.
1 & 3 looks more like some "magic", so I think you should try to follow 2 first. Hope this will help you.

Not sure if this would solve your problem, but try calling processPendingChanges on context B, after changes from A are being merged.
Also, does your nsfrc use caching? Try disabling the cache and see if it makes any difference in your case...

Related

NSManagedObjectContextObjectsDidChangeNotification deleted objects issue

It appears as though I've exposed a strange issue where my deleted objects are not actually deleted. This only occurs when I'm responding to theNSManagedObjectContextObjectsDidChangeNotification.
More specifically: I have a list of A <->>B. A cascade deletes 'owned' instances of B.
When I delete an instance of A, it's 'owned' B instances are deleted, but the A instance is not deleted.
When I invoke [context processPendingChanges]; at the top of my notification handler, the issue seems to only occur when I delete the last object. Otherwise, instances of A tend to stick around.
So, it seems that the root of the issue is in something I don't understand about Core Data. Figuring out why has been unproductive. Can anyone list the reason(s) I might see this behavior?
I found two reasons this can happen:
A) In my case my first strategy was to build temporary graphs of the deleted managed objects as an organizational convenience. You must modify deleted objects in a temporary managed object context, or else any changes you make to the deleted objects supersede the deletion. Reset the temporary context as soon as you're done operating on the (stale) deleted managed objects.
B) The objects must have a reference count of 0 when deleted. This means they have to be released from all fetched results controllers, NSArrays, etc. prior to deletion.
Satisfying A and B solved this issue.

Core Data: Deleting causes 'NSObjectInaccessibleException' from NSOperation with a reference to a deleted object

My application has NSOperation subclasses that fetch and operate on managed objects. My application also periodically purges rows from the database, which can result in the following race condition:
An background operation fetches a bunch of objects (from a thread-specific context). It will iterate over these objects and do something with their properties.
A bunch of rows are deleted in the main managed object context.
The background operation accesses a property on an object that was deleted from the main context. This results in an 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault'
Ideally, the objects that are fetched by the NSOperation can be operated on even if one is deleted in the main context. The best way I can think to achieve this is either to:
Call [request setReturnsObjectsAsFaults:NO] to ensure that Core Data won't try to fulfill a fault for an object that no longer exists in the main context. The problem here is I may need to access the object's relationships, which (to my understanding) will still be faulted.
Iterate through the managed objects up front and copy the properties I will need into separate non-managed objects. The problem here is that (I think) I will need to synchronize/lock this part, in case an object is deleted in the main context before I can finish copying.
Am I missing something obvious? It doesn't seem like what I'm trying to accomplish is too out of the ordinary. Thanks for your help.
You said each thread has its own context. That's good. However, they also need to stay synchronized with changes to each other (how depends on their hierarchy).
Are the all assigned to the same persistent store coordinator, or do they have parent/child relationships?
Siblings should monitor NSManagedObjectContextObjectsDidChangeNotification from other siblings. Parents will automatically get notified when a child context saves.
I ended up mitigating this by perform both fetches and deletes on the same queue.
Great question, I can only provide a partial answer and would really like to know this too. Unfortunately your own solution is more of a workaround but not really an answer. What if the background operation is very long and you can't resort to running it on the main thread?
One thing I can say is that you don't have to call [request setReturnsObjectsAsFaults:NO] since the fetch request will load the data into the row cache and will not go back to the database when a fault fires for one of the fetched objects (see Apples documentation for NSFetchRequest). This doesn't help with relationships though.
I've tried the following:
On NSManagedObjectContextWillSave notification, wait for the current background task to finish and prevent new tasks from starting with something like
-(void)contextWillSave:(NSNotification *)notification {
dispatch_sync(self.backgroundQueue, ^{
self.suspendBackgroundOperation = YES;
});
}
Unset suspendBackgroundOperation on NSManagedObjectContextDidSave notification
However the dispatch_sync call introduces possible dead locks so this doesn't really work either (see my related question). Plus it would still block the main thread until a potentially lengthy background operation finishes.

Core Data: is there a way to move/copy a NSManagedObect to another context?

I've got a NSManagedObject in Context A and would like to have it in Context B. If I use the objectID to load it in Context B, all the data in context A is lost, so I would like to copy/move the data, something like
[managedObjectInContextA copyTo:contextB];
How can I achieve this?
Thanks a lot,
Stefan
Three steps:
early on, register somewhere appropriate for the NSManagedObjectContextDidSaveNotification. (The view controller that owns context B might be a good choice; it depends on your app architecture.)
Make changes to context A's copy of the object, and save context A. This causes the above notification to fire.
in the method that receives this notification, call mergeChangesFromContextDidSaveNotification: on context B. This causes context B to get in sync with the freshly saved changes.
Context B's copy of the object now has the new state.
If both copies of the object might have unsaved changes, things get more complicated. See NSManagedObjectContext's mergePolicy to see ways of handling that if you need to.
The most obvious solution would be to just save the data to the persistent store before switching contexts.
[managedObjectContextA save:&error];

Silverlight Multithreading; Need to Synchronize?

I have a Silverlight app where I've implemented the M-V-VM pattern so my actual UI elements (Views) are separated from the data (Models). Anyways, at one point after the user has gone and done some selections and possible other input, I'd like to asyncronously go though the model and scan it and compile a list of optiions that the user has changed (different from the default), and eventually update that on the UI as a summary, but that would be a final step.
My question is that if I use a background worker to do this, up until I actually want to do the UI updates, I just want to read current values in one of my models, I don't have to synchronize access to the model right? I'm not modifying data just reading current values...
There are Lists (ObservableCollections), so I will have to call methods of those collections like "_ABCCollection.GetSelectedItems()" but again I'm just reading, I'm not making changes. Since they are not primitives, will I have to synchronize access to them for just reads, or does that not matter?
I assume I'll have to sychronize my final step as it will cause PropertyChanged events to fire and eventually the Views will request the new data through the bindings...
Thanks in advance for any and all advice.
You are correct. You can read from your Model objects and ObservableCollections on a worker thread without having a cross-thread violation. Getting or setting the value of a property on a UI element (more specifically, an object that derives from DispatcherObject) must be done on the UI thread (more specifically, the thread on which the DispatcherObject subclass instance was created). For more info about this, see here.

Resolving an NSManagedObject conflict with multiple threads, relationships, and pointers

I'm having a conflict when saving a bunch of NSManagedObjects via an outside thread. For starters, I can tell you the following:
I'm using a separate MOC for each thread.
The MOCs share the same persistent store coordinator.
It's likely that an outside thread is modifying one or many of the records that I'm saving.
OK, so with that out of the way, here's what I'm doing.
In my outside thread, I'm doing some computation and updating a single value in a bunch of managed objects. I do this by looking up the object in the persistent store by my primary key, modifying the single decimal property, and then calling save on the bunch all at once.
In the meantime, I believe the main thread is doing some updating of its own.
When my outside thread does its big save on its managed object context, I get an exception thrown stating a large number of conflicts. All of the conflicts seem to be centered around a single relationship on each record. Though the managed object in the persistent store and my outside thread share the same ObjectID for this relationship, they don't share the same pointer. Based on what I see, that's the only thing that's different between the objects in my NSMergeConflict debug output.
It makes sense to me why the two objects have relationships with different pointers -- they're in different threads. However, as I understand it from Apple's documentation, the only thing cached when an object is first retrieved from the persistent store are the global IDs. So, one would think that when I run save on the outside thread MOC, it compares the ObjectIDs, sees they're the same, and lets it all through.
So, can anyone tell me why I'm getting a conflict?
Per the documentation in the Concurrency with Core Data chapter of The Core Data Programming Guide, the recommended configuration is for the contexts to share the same persistent store coordinator, not just the same persistent store.
Also, the section Track Changes in Other Threads Using Notifications of the same chapter states if you're tracking updates with the NSManagedObjectContextDidSaveNotification then you send -mergeChangesFromContextDidSaveNotification to the main thread's context so it can merge the changes. But if you're tracking with NSManagedObjectContextDidChangeNotification then the external thread should send the object IDs of the modified objects to the main thread which will then send -refreshObject:mergeChanges: to its context for each modified object.
And really, you should know if the main thread is also performing updates through its controller, and propagate its changes in like manner but in the opposite direction.
You need to have all your contexts listening for NSManagedObjectContextDidSaveNotification from any context that makes changes. Otherwise, only the front context will be aware of changes made on the background threads but the background context won't be aware of changes on the front thread.
So, if you have three threads and three context each of which makes changes, all three context must register for notifications from the other two.
Unfortunately, it seems as though this bug was actually being caused by something else -- I was calling the operation causing the error more than once at the same time when I shouldn't have been. Although this doesn't answer the initial question as to why pointers matter in conflicts, updating my code to prevent this situation has resolved my issue.

Resources