Let's say we have two entities in a Core Data model: Departments and Employees.
The Department has a one-to-many relationship to Employees.
I have the following ManagedObjectContexts:
- Root: connected to the Persistent Store Coordinator
- Main: context with parent Root
When I want to create an Employee I do the following:
- I have a Department in the Main context
- I create an Employee in the Main context
- I assign the Department to the Employee's department property
- I save the Main context
- I save the Root context
This creates a retain cycle both in the Main context and in the Root context.
If I did this without a child context (all in the Root context), then I could break the retain cycle by calling refreshObject:mergeChanges
on Employee. In my situation with the two contexts I could still use that method to break the cycle on the Main context, but how am I going to break the cycle on the Root context?
Side note: this is a simple example to describe my problem. In Instruments I can clearly see the number of allocations growing. In my app I have contexts that go deeper than one level, causing an even greater problem, because I get a new entity allocation with retain cycle per context I'm saving.
Update 15/04: NSPrivateQueueConcurrencyType vs NSMainQueueConcurrencyType
After saving both contexts I can perform refreshObject:mergeChanges
on the Main context with the Department object. This will, as expected, re-fault the Department object, break the retain cycle and deallocate the Department and Employee entities in that context.
The next step is to break the retain cycle that exists in the Root context (saving the Main context has propagated the entities to the Root context). I can do the same trick here and use refreshObject:mergeChanges
on the Root context with the Department object.
Weird thing is: this works fine when my Root context is created with NSMainQueueConcurrencyType (all allocations are re-faulted and dealloced), but doesn't work when my Root context is created with NSPrivateQueueConcurrencyType (all allocations are re-faulted, but not dealloced).
Side note: all operations for the Root context are done in a performBlock(AndWait) call
Update 15/04: Part 2
When I do another (useless, because there are no changes) save or rollback on the Root context with NSPrivateQueueConcurrencyType, the objects seem to be deallocated. I don't understand why this doesn't behave the same as NSMainQueueConcurrencyType.
Update 16/04: Demo project
I've created a demo project: http://codegazer.com/code/CoreDataTest.zip
Update 21/04: Getting there
Thank you Jody Hagings for your help!
I'm trying to move the refreshObject:mergeChanges
out of my ManagedObject didSave
methods.
Could you explain to me the difference between:
[rootContext performBlock:^{
[rootContext save:nil];
for (NSManagedObject *mo in rootContext.registeredObjects)
[rootContext refreshObject:mo mergeChanges:NO];
}];
and
[rootContext performBlock:^{
[rootContext save:nil];
[rootContext performBlock:^{
for (NSManagedObject *mo in rootContext.registeredObjects)
[rootContext refreshObject:mo mergeChanges:NO];
}];
}];
The top one doesn't deallocate the objects, the bottom one does.
performBlock
wraps a complete "user event" butperformBlockAndWait
does not. – Anytime