Child NSManagedObjectContext inconsistent NSFetchedResultsController results after saving - ios

I have a very peculiar.. issue happening with my NSFetchedResultsController.
My setup is using Magical Record, I have a child with the parent set as MR_defaultContext ([NSManagedObjectContext MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]]).
I create a nested context using the first view controller's managedObjectContext as the parent, use that for the next page, which is modal. This next page executes a fetch with a predicate, simple, finding all the entities in a to-many relationship.
Now, if I do not save the inserted entities prior to pushing to the next modal page, the fetch is correct. But.. if I save with [self.managedObjectContext MR_saveOnlySelfWithCompletion] and then push, the fetch is sometimes correct, sometimes (most times) random and incorrect. For example there are 5 entities it should be fetching, but it fetches 1, 3, 4, sometimes none. Very odd!
Even more odd is I keep the number of entities as a variable in the modal page, which shows 5 correctly. Always. And if I print out the parent entity of the to-many relationship, it does have all 5 relationships set (and the inverse is set correctly as well).
I have read about fetching the permanent IDS before saving, but that did not make a difference.
Anyone know what is going on?

I guess that your issue might be caused by the fact that you are moving to the next modal page before MR_saveOnlySelfWithCompletion is done.
Try to push the next modal from within the completion block you pass to that method and everything should work.

Ok, I was thinking about the child/parent relationship wrong. I watched the Core Data Best Practices from WWDC 2012, and sorted out what was going on.
I initially thought saving was necessary for the child to be able to access parent changes. Turns out that is not true - saving prior to the segue was unnecessary for the child context to access the changes on the parent context. So removing the save will have to do as it is not needed.
As an aside, I was using existingObjectWithID to get the object from the next modal page, which was unnecessary, as the changes from the parent context would be available to the child. I am not 100% sure why the object would not be fetched by this method as the documentation states If there is a managed object with the given ID already registered in the context, that object is returned directly which seems to me like it should be.
As another aside - saving the context from the child after pushing to modal (before the fetch) works just fine..
Still not really sure why the inconsistency with the fetch after the save from the parent VC.

Related

What happens to the other NSManagedObjects on a context when context.reset() is called?

I have a scenario where an Entity has many relationships with other entities. I did some changes in the NSManagedObject of the entity and discarded those changes.
Right now I'm calling managedObjectContext.refresh(entity, mergeChanges: false) and then managedObjectContext.refresh(relatedEntity, mergeChanges: false) on every related entity to ensure having no dangling objects in the context.
What would be the difference if I directly call managedObjectContext.reset()? Should I still need to refresh or mark nil the related entities?
Is there any way to make this code more optimized?
If you call reset, you need to also immediately stop using every managed object you have fetched from that context. All fetches need to be redone after the reset, because it makes the context forget about everything it has already fetched.
There are various patterns for more efficiently creating discardable changes like you describe. One popular choice is:
Create a new managed object context and make it a child context of the current context.
Make your changes in that context.
If you want to save changes, save them. If not, just don't bother. The child context will be deallocated and its changes will be lost.

NSManagedObjectContext refreshObject causes duplicates in NSFetchedResultsController

I have an issue where if I make changes to a core data object, save then refreshing the object, causes my NSFetchedResultsController to show a duplicate object. I think I understand what's going on, but I'm looking for someone to confirm, and also to hopefully give some more detail as to why.
To explain in more dtail I have two entities, Fixture and Position. A Fixture has many Positions, and a Position belongs to only one Fixture. To reproduce the issue I do the following:
Fetch all Positions.
Modify some value (any one) on that objects Fixture. I.E foo.fixture.name = "foobar"
Save the context
Refresh objects by calling context.refreshAllObjects, or context.refreshObject(foo, mergeChanges: false/true).
I have a tableview using a fetched results controller which displays Fixures. After doing the above the tableview will display duplicates for each item (it doesn't matter if I use the delegate methods of the FRC to do the update or I just reload the tableview).
It appears what's happening is that the refresh invalidates the objects that the FRC knows about, while at the same time gets knowledge of another set of objects. If, as step #5, I call frc.performFetch() then the problem goes away.
Other things to note:
No matter how many times I run the code I only get two of each object (I'm using a random button to trigger it for testing).
init(entityName, context) is called on my Fixture subclass as soon as I access the Fixture property of my object during the next code run (i.e after refresh was called).
In my sample everything is taking place on the same context (though it happens with child contexts as well)
To give some more context as to how I got myself in this situation in the first place users can click on a fixture in the list and then ultimately narrow down on a single position a few screens later where they can perform actions that modify the fixture. There are other active areas of the application at this point that are listening to the NSManagedObjectContextDidSaveNotification and I want them to update their objects so they can display the correct data, which is why I was calling refresh.
I've dug around in the docs and I can't see anything that specifically explains my theory that updating the context causes NSFetchedResultsController to have invalid objects. Can anyone shed some light on this behavior?
First, you really should not be overriding -init... on a NSManagedObject. That is one of the methods that you are strongly discouraged from overriding and can very easily be the source of your issues.
Second, your entire UI should be using a single instance of the NSManagedObjectContext that is associated to the main queue and therefore there should only be ONE instance of any particular entity in your UI. If you have multiple contexts you are just making things more complicated for yourself. If you are using a single context keep in mind that no matter how many times you fetch an object against that context you will get the exact same pointer back.
The NSFetchedResultsController will never create objects, it only fetches and provides them for display. Therefore the NSFetchedResultsController is only reporting the fact that you have created this duplication somewhere else in your code.
Now some questions, do these duplicates get pushed down to disk?
Can you see them in the store file on disk?
When you re-launch your application are the duplicates still there?
When you put a print in your custom -init methods on the NSManagedObject; do they fire more than once?

breeze: behavior of setDeleted

I have a grid which shows a list of entities.
Each row has a delete button.
Once the user clicked on the delete button for a given entity I want to change the css of the row and to replace the delete button with a cancel button.
So on the delete button event handler, I do :
myEntity.entityAspect.setDeleted();
But as soon as I do that, the entity is removed from the collection and the row disappear from the grid.
Is there a way to prevent that ? I just want to mark the entity as 'deleted', and postpone any change until user clicks on save button.
The only alternative I see, is to add a property isDeleted to my client-side model and to base my logic on that. But it means I have to handle change tracking myself and loop through the entities on save to call setDeleted for entities which isDeleted property is true. I'm not fond of this solution. Is there something better I'm missing with breeze ?
My guess is that your collection is the value of one of Breeze's navigation properties. These collections are 'live' in that if you delete an entity, the entity becomes removed from all live collections to which it belongs.
So the best answer would be to copy the entities into your own collection and bind to that. Your own collection will not be 'live' and deleting entities will not remove them from the collection.
The problem with 1) is that when you delete an entity we need to sever its relationships with any other entities. This really is the essence of deleting, we want the environment to look like it will after the delete is committed ( via EntityManager.saveChanges())
When you say that you do not want the navigation properties to be set to null after a delete I am assuming that you are talking about a scalar (1->1) or (n->1) navigation. (nonscalar navigations simply remove the entity from the collection) The first issue is whether you are talking about navigations 'To' the deleted entity or 'From' the deleted entity. In the case of 'NOT" removing the navigation 'to' a deleted entity we would be causing many if not most existing breeze apps to fail. Most apps expect to see entities disappear when you delete them.
In the case of 'NOT' removing the navigation 'from' a deleted entity, the concept make more sense except that you cause two more issues. The first is that this breaks any 'bidirectional' navigations, i.e. you can no longer roundtrip between the 'from' and 'to' navigations. But more importantly, we view a 'delete' as a precursor to an eventual 'detach' operation ( which will occur after a successful save). The primary value of the 'detach' is that it recovers memory because we have effectively removed any references to the 'deleted' object, which means that it can be garbage collected. If we leave any navigation properties intact this garbage collection will never occur, and we end up with a memory leak.
We could get around this by having the 'detach' operation also remove navigation properties but the rules begin to get harder to explain.
If you still feel strongly about this, please post your suggestion to the Breeze User Voice. If we see some substantial interest in this, we will try to come up with a solution, but right now we don't have a good answer to this that doesn't add real conceptual complexity. (Something we really try to avoid).
Can you give more detail about the 'validations' that you are seeing? Are these validations on the 'deleted' entity or on the still alive entities that were pointing to the deleted entity? Not having the first occur makes a lot of sense (and we should fix it if this is the case), not having the second occur does not make sense because you really are causing a real validation failure.
Ok, I have tried Jay's solution. First I do a deep copy using lodash.js:
$scope.sourceMaterials = _.clone($scope.request.sourceMaterials, true);
then I bind my grid to $scope.sourceMaterials. User clicks on delete button for a given row and I do:
var document = $scope.sourceMaterials[index];
document.entityAspect.setDeleted();
The problem is that breeze sets to null all the navigation properties of my entity (see removeFromRelationsCore function in breeze.js).
Doing so affects the values shown on screen to the user. For instance one of the required field gets nulled by breeze. So when user clicks on the save button, the validation fails.
To sum up I have two issues:
navigation properties should not be set to null when deleting my entity
the validation should not be triggered for an entity that has its state set to deleted
Could you shed some light on this please ?
EDIT
Following Jay's answer, I can confirm that:
1) navigation property is nulled FROM the deleted entity and I'd like that to be prevented somehow
ex: I have an entity Document with a 1 -> 1 np named DocumentType. If I set deleted on Document, then DocumentType is nulled.
2) validation rules occur on properties of the deleted Entity (those that were nulled)
which one of these problems should I report on user voice ?

Why do I need to remove relationships before deleting an object in core data?

I have a program in which I'm using CoreData to manage both permanent and temporary data.
In one instance, I abort the addition of some data by deleting the object when the user presses cancel. But when I hit the next view, the data is still there, attached to it's parent core data object.
[self.bar removeFoosObject:self.foo];//Why do I need this line?
[self.foo.managedObjectContext deleteObject:self.foo];
I eventually solved this by manually removing the child object from it's parent -- but isn't that something core data handles automatically? Why should I need the first line?
I ran some test code, and Foo is definitely deleted -- the code that it was mucking up let me check, and it's MOC has been set to nil. The memory exists, but it should be very, very dead...
You have to manually do this because you have your deletion rules set wrong. Check the Relationship Delete Rules in the following apple documentation.
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html
Or it could also be that the relationship is NOT set as optional between the parent and child object.
And also after you delete the object you should save the database to synchronize it.

How to make child MOC see changes of parent MOC and how to resolve conflicts between them?

It seems the new queue based MOC together with nested MOC introduced in iOS 5 serve as an simpler and cleaner concurrency model, so I'm happily using it now. But there are several things that are not clear to me:
Do child MOCs pulls parent MOC's changes automatically? Or do I still need to coordinate them manually? If the latter, how to manually refresh the children to incorporate parent changes?
(I know how things work the other way around -- parent MOC gets changes from a child MOC when the child saves.)
What will happen on saving a child MOC if there are conflicts between changes in the child and changes in parent? Is mergePolicy also responsible for resolving conflicts between child MOC and parent MOC?
Yes and no, depending on your definition of "pulls parentMOCs changes automatically." If you mean, the next time a child fetches, it will get the updated data, then yes. If you mean will objects automatically get changed, then no. The reason is that a MOC is a scratchpad, and it will not change without you changing it.
Yes, you will have to resolve merge conflicts if you are making changes to the same objects/relationships from multiple places. This is a complex issue, and can not be sufficiently answered in a SO answer. You should really read the Core Data Programming Guide, especially the section on Change Management.
EDIT
Say I'm holding object A in child MOC and add a relationship B to A in parent MOC. What do I get if I access A.B? nil? What should I do if
I want to see A.B in child MOC? Use refreshObject:mergeChanges: to
refresh A, or use objectWithID: to fetch A again? Is there any way to
refresh the whole child MOC? I have a very complex object network in
child MOC and I don't want to(or just can't) refresh/re-fetch every
object one by one.
When looking at object A through the child MOC, you will NOT see the change in the parent MOC until you refetch from the child MOC. If you want to see what's in the parent, you can do so via another fetch on the MOC, or through any of the methods to get a apecific object from the store. However, I must stress that you need to read the documentation about each fetch, because they may have side effects. The exact answer depends on which approach you choose.
Typically, if you are doing continual change, you want to either make changes in a child, and push them to the parent, or handle the DidChange notification to make the changes seem "automatic."
Does mergePolicy work here?
Yes.
Again, the best answer I can give you here on SO is to point you to the "Core Data Programming Guide." Pay special attention to these two sections:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html#//apple_ref/doc/uid/TP40002484-SW1
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdChangeManagement.html#//apple_ref/doc/uid/TP30001201-CJBDBHCB

Resources