7
votes

Is there a safe way to remove the persistent store (and create a new one) in an application, where other threads are using NSManagedObjectContext's associated with the store being removed? I have tried locking the NSPersistentStoreCoordinator and unlocking it after the operation is over, but it didn't help - all my attempts have resulted in a deadlock. It always happens on this line (executed on the main thread):

[self.persistentStoreCoordinator removePersistentStore: store error: &error];
3
Can you post images of the stack traces of the threads involved? - Leo Natan
Are you using thread confinement or queue confinement? Are you using parent-child contexts? - quellish
You first should determine why you want to do this, and see if you can come up with another solution. In most cases, you are better off just tearing down your entire stack, and make sure anything holding a managed object listens to notifications about the stack going away so they release their references. What you are trying to do is similar to ripping the first floor out of a skyscraper, and expecting everything to remain as it was beforehand. - Jody Hagins

3 Answers

2
votes

I haven't tried this, but from the docs on moc setPersistentStoreCoordinator: ...

The coordinator provides the managed object model and handles persistency. Note that multiple contexts can share a coordinator.

This method raises an exception if coordinator is nil. If you want to “disconnect" a context from its persistent store coordinator, you should simply set all strong references to the context to nil and allow it to be deallocated normally.

This would suggest that the safe way to remove the psc is to first have every thread with a moc release it (nil-out reference to it in ARC), then perform the removePersistentStore:

1
votes

I would try using an approach described here (section Parent/Child Contexts): Multi-Context CoreData

Basically, your PSC has only one MOC associated with it (the parent MOC). Other threads have their own MOCs, with their parentContext set to the main MOC (the one associated with the PSC).

Then you could try something like this:

// Save each child MOC
for (NSManagedObjectContext *moc in self.someChildMOCs)
{
   [moc performBlockAndWait:^{

       // push to parent
       NSError *error;
       NSAssert([moc save:&error]);
       moc.parentContext = nil;
   }];
}

// Save parent MOC to disk
[self.mainMOC performBlockAndWait:^{
   NSError *error;
   NSAssert([mainMOC save:&error]);
}];

[self.persistentStoreCoordinator removePersistentStore:store error:&error];
mainMOC.persistentStoreCoordinator = nil;
1
votes

Is there a safe way to remove the persistent store (and create a new one) in an application, where other threads are using NSManagedObjectContext's associated with the store being removed?

It should be safe if you can guarantee that no live managed objects will attempt to access the persistent store. This part is crucial: you must ensure that there are no live objects of any kind that have been loaded from or otherwise associated with the old persistent store.

You can do this by resetting each managed object context:

[managedObjectContext performBlockAndWait:^{
    [managedObjectContext reset];
}];

Once you have done this for every managed object context, you can remove the persistent store.

Note that if you have any managed objects that were fetched from those contexts, you must dispose of them immediately without reading or writing attribute values or using them in any way. These objects might need to use the context for a variety of reasons, but after calling reset the context doesn't know anything about them anymore. Get rid of them immediately (ideally even before calling reset), because they're time bombs waiting to blow up your app the moment you touch them.