Core Data and Parent Contexts - ios

I am also interested in understating how iOS 5+ parent contexts work. I am using RestKit 0.2x but I think these are general Core Data questions.
Let's say we have this store coordinator/contexts hierarchy:
Persistent Store Coordinator (PSC) -> PS Managed Object Context (PS-MOC) -> Main Queue MOC (MQ-MOC).
For a request RestKit creates a temporary Private Context (P-MOC) from MQ-MOC and on success by default saves the changes back all the way to the PSC:
PSC -> PS-MOC -> MQ-MOC [-> P-MOC]
I also manually create managed objects and modify properties directly on the MQ-MOC.
My questions:
Are my unsaved MQ-MOC changes passed to child P-MOQ when created?
Should I save my MQ-MOC changes before a child P-MOQ gets created? Should I save these changes all the way to the PSC?
If P-MOQ is configured to save only to its parent MQ-MOC context, the newly introduced changes look like my MQ-MOC unsaved changes?

Are my unsaved MQ-MOC changes passed to (an existing) child P-MOQ?
Unsaved, never. Saved, no, because they aren't required there so RestKit doesn't check and merge the changes.
How about unsaved changes when the child P-MOQ is created?
Anything saved in the parent when the child is created will be available in the child.
Should I save my MQ-MOC changes before a child P-MOQ gets created?
If the child needs them, yes (though in theory RestKit should detect that an object you use in a request isn't saved and save it for you, best not to rely on that).
Should I save these changes all the way to the PSC?
Yes. Having things saved but not persisted will just lead to issues in the future.
If P-MOQ is configured to save only to its parent MQ-MOC context, the newly introduced changes look like my MQ-MOC unsaved changes?
A child will push changes up to its direct parent but no further, so in effect, yes.
Just to check, you should be aware of the RestKit added method saveToPersistentStore:.

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.

Parent Child Context Confusion & Concurrency issues

Am having few doubts with parent child context set-up and how the data behaves. Can anyone please suggest a link where i can find proper explanation on how they work.
Few Points & Observation
Parent context acts as store for child context :- But the changes in the parent are not reflected in the child.
Question 1: If parent context acts as store for child context then why does the changes in the parent are not reflected in the child context, while the child fetches them.
Observations :
Insert in parent :- Child fetch requests get those objects.
Update/delete a object in parent :- Then the fetch request do not get the updated object instead it provides us with the object in child context, so if we need the updated object we have to either reset the context but then we will lose all the changes in the child context or refresh the object.
Question 2: Child overwrites parent context values. So if the user has modified something in parent context and child has also modified the same thing then the updates of user will be lost. This is not much problem until relationships are involved.
Say there is one to many relationship between entity A,B (A->>B).
Parent Context : Inserts a new child to entity A say Child-1.
Child Context : Deletes both entity A and all its child.
So now when the child saves to parent context, then it deletes entity A and its old child, the newly inserted child-1 remains in the context with null referencing parent.
How can i resolve this issue?
Question 1
Because you have already fetched the data, if you want to force a fetch as you said you should either reset the context or refresh the object.
Question 2
You can try to use cascade delete if you want to always delete entities B when the parent entity A is deleted.

How do parent/child Managed Object Contexts exactly work?

My app periodically request updates for the objects I persist with Core Data to a web service. I then need to update the objects I have in my main context (the one provided in AppDelegate by default). It is not the user who edits the objects, so I need to avoid blocking the UI, and furthermore I'm not just modifying objects information, but deleting and adding new objects if needed.
It seems that there are two options to perform updates of NSManagedObject objects: creating a "sibling" context in a private queue, and creating a child context. Reading several posts, there are more people saying that it is better to use parent/child contexts (correct me if I'm wrong), but I don't completely understand how that works. I have some questions regarding parent/child contexts:
Can a child context be in a private queue and the parent context in the main queue?
I've read somewhere something about setting a merge policy, but I didn't find examples of its use, maybe is not necessary to set a merge policy when usen parent/child contexts? When would they be set? Where can I find an example or tutorial?
If I set my private context as child of the main context, and I save the child private context, will the objects in private context "replace" the objects in the main context as I want? (including deleting the objects that are no longer present in the private context and the new ones)... I mean... does the whole child context replace the whole parent context?
Would it be better to save the private context without being a child of the main one, and then clearing and refetching all the new data in the main context?
I really need help with this issue, thanks in advance.
Yes the child can be in a private queue and the parent in main, and the opposite way is valid too.
Merge policy...
You use a merge policy object to resolve conflicts between the persistent store and in-memory versions of managed objects.
So you would most often use this when you have multiple contexts coming directly from the persistentStoreCoordinator, perhaps one Private Queue type and one Main Queue type.
The merge policy affects the result of calling
- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification
So if your deleting actions were done in the Private Queue and then saved, you could reflect that change in your UI by calling mergeChanges... on your Main context with the notification object from the
NSManagedObjectContextDidSaveNotification that the save on the Private context would create.
The parent context isn't replaced. Simply, the changes made in the child are overlaid onto the parent. So in your case deleting an object in the child would cause that object to delete once you saved the child.
No. You'd be best to do what was described in 2 if you didn't want to use parent - child contexts. What you describe is discarding your current Main context and creating a new one to reflect the new state of the PSC. Its not awful but it is expensive and if your UI has any tight binding to managed objects you need to make sure those objects are reset also. Its cheaper just merge the save.
Using mergeChangesFromContextDidSaveNotification: has been measured to be quicker than parent->child for change propagation but possibly you may find parent->child to be clearer in a readability sense.

Deleting NSManagedObjectContext

I am trying to make an application where a user can edit attributes of a managedObject in a view, and select either Done to persist the changes, or Cancel to undo the changes.
To implement this behavior, I plan to do the following-
When the view controller is loaded to edit the managedObject, create a backupManagedObject which is a clone of the original managedObject.
This backupManagedObject is created in a separate child ManagedObjectContext.
Let the user edit the original managedObject.
If:
Done is pressed, the original managedObject is persisted, and backup is deleted
Cancel is pressed, the backupManagedObject is cloned into original managedObject and the backup is deleted
My question is, once I am done with the backupManagedObject, how can I delete the childManagedObjectContext which will have no more managed objects and I don't plan to use them anymore (for every new view controller, I plan to just create a new child managed object context and destroy it once view controller is done).
You should do this the other way around:
When you load your editing View Controller, create new Managed Object Context that is the child of your main one, let's call it the editingMOC.
Do the edits to the editingMOC, and if you want to persist them, save the editingMOC (this will propagate changes to the mainMOC), and then save the mainMOC to save the data to the persistent store.
If you wish to discard the changes done to the editingMOC, simply do not save them and let the context get dealloc'd.
In general, a managed object context is release the same way any other object in Objective-C is released and deallocated.
If you are using ARC, simply set the property to nil when you no longer need it and it will be destroyed along with any unsaved changes.
However, your approach for this problem is a bit complicated.
You could simply create a new "editing" child context, fetch the objects you like to edit in that context and make changes to the objects.
If the user decide to commit the changes, save the context (up to the store), and if not, simply destroy the context.
An easier way is to simply create the view and populate the UI controls (text fields, etc.) using data from the NSManagedObject attributes. If the user makes any edits then set a flag so you know if changes are made and then when they select Done update the NSManagedObject attributes using values from the UI controls and save the MOC. If they select Cancel then don't do anything.
See this link for a video showing an app using a similar approach for editing Core Data records on iOS. The OSX app uses standard NSPersistentDocument undo manager.
http://ossh.com.au/design-and-technology/software-development/uimanageddocument-icloud-integration/os-x-and-ios-app-integration-with-core-data-and-icloud/

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