Strange parent / child NSManagedObjectContext phenomenon
Asked Answered
H

2

10

I have created two context like this:

// create writer MOC
_privateWriterContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateWriterContext setPersistentStoreCoordinator:_persistentStoreCoordinator];

// create main thread MOC
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = _privateWriterContext;

I have a NSFetchResultedController initiated with _managedObjectContext.

I know it is strange, but I am adding a record to the parent to _privateWriterContext, I am saving it.

What is surprising is that child context and so FRC gets notified about this event. Why? I have not reset-ed child, or anything else. I thought they are independent entities as long as child context will not get saved.


In @pteofil article I found this line:

When a change is made in a context, but not saved, it is visible to all of its’ descendants but not to its’ ancestors.

.. it is pushed to the persistent store (via the persistent store coordinator) and becomes visible to all contexts connected to the store.

Haematoblast answered 3/5, 2015 at 16:13 Comment(10)
According to this article benedictcohen.co.uk/blog/archives/308, that's normal behavior.Anh
And another very good article about the performance of different managed context setups: floriankugler.com/2013/04/29/…Anh
I see, do you think it is somehow possible prevent changes to propagate into child context? I thought changes will not propagated until I observe NSManagedObjectContextDidSaveNotification and merge changes from notification if I am using two context joining to the same PSC configurationAudet
I believe that's the whole idea of parent-child relationship, and it's designed to work that way. Why don't you just have 2 separate managed contexts, and as you say you observe NSManagedObjectContextDidSaveNotification to merge changes between them?Anh
on separate context you meant, I should create MOCs in the old way, not with initWithConcurrencyType but with init, and then this statement in article will not take place, it is pushed to the persistent store and becomes visible to all contexts connected to the store.Audet
Yes, that's what I meant, to create MOC the old way.Anh
A save pushes one level down (child pushes to parent) and one level down only. A save in a parent context would not typically cause a change in the child unless the child's staleness interval was a very low value.Month
It is not recommended to use NSManagedObjectContextDidSaveNotification with nested contexts. The parent-child relationships replace notifications as a change distribution mechanism, mixing the two can cause problems.Month
Can you make the write-context a child of the main-context? That way, new items will not notify the main-context (as it is higher up in the hierarchy). When you then call save on the write-context, it will save up to the parent, which is the main-context.Baton
@Haematoblast - I was referred to this question, since the question and follow-up comments seem to indicate that one would expect a child context to be notified when a parent context changes and/or saves. This is not how it is supposed to work, and I do not believe it works that way. If a child refreshes/refetches, or fires a fault, it will get the information from the parent, but the child context will not be notified. Furthermore, unless modified, the FRC does not register for change/save notifications from its parents. Thus, I am interested in any additional information you have on this topic.Statesman
B
0

This is not supposed to happen. Adding an NSManagedObject ('record' ) to the parentContext, will not make the child aware of this object automatically. Only when you make the childContext execute a fetch, then it will fetch from the parentContext. To figure out what is going on in your case, here are some suggestions:

  • figure out when a fetch is executed on the childContext (this is done by the fetchedRestultsController when you set it up. Check if that fetch happens before or after you add the managedObject to the parentContext).
  • set breakpoints in all four delegate callbacks of the fetchedResultsController to find out for which object it is calling the methods (and see if it is the object you just added to the parentContext).
  • make absolutely sure you know which context you are sending messages too.

I have a used a similar approach, but different: the childContext is the context is used to parse in new data (on a private queue), and when this parsing is done, the chid calls save:. This will save the changes up to the parent, which is in my case the mainQueueContext. This call to save: will cause the mainQueueContext to receive all the newly parsed objects, and any fetchedResultsController using that mainQueueContext will then call it's delegate methods for the new/changed/updated/delete objects. You could try inverting your child/parent relationship too and see if it works as described in the docs, just to find out whats going on.

Baton answered 31/12, 2015 at 6:55 Comment(0)
T
0

I strongly recommend avoiding parent-child context setups. Our book goes into detail why they often lead to strange behaviour: https://www.objc.io/books/core-data/

Short story: They're not as independent as you might think.

Use multiple context that share a single persistent store coordinator if you need more than a single context.

Tifanytiff answered 4/1, 2016 at 21:55 Comment(1)
I am using a context with NSMainQueueConcurrencyType and another with NSPrivateQueueConcurrencyType. They each use their own NSPersistentStoreCoordinator, but point to the same store (NSSQLiteStoreType). Why would I use a single NSPersistentStoreCoordinator?Hydrochloride

© 2022 - 2024 — McMap. All rights reserved.