views:

1183

answers:

2

Hi everybody,

Basically, what I'm trying to do is to wipe out all data in my CoreData persistent store, then import new data. How would you do this? It seems that the simplest solution is call [NSPersistentStoreCoordinator removePersistentStore:error:] and then remove the file. Is that the best practice available? Is it thread-safe?

Thank you very much,

#

Question 0.1: was

I am trying to update the data in a CoreData persistent store. My user is seeing a table view with statistical data. I want to update the application by deleting all existing data, then importing new data. I would like to show a progress view to tell the user that the application is not hanging.

I have added the following resetPersistentStore method in my AppDelegate (persistentStoreCoordinatoris given for reference):

// ...
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
// ...

/**
 Returns the persistent store coordinator for the application.
 If the coordinator doesn't already exist, it is created and the application's store added to it.
 */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: kPersistentStoreFilename]];

 NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
  NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
  abort();
    }    

    return persistentStoreCoordinator;
}

/**
 * Will remove the persistent store
 */
- (NSPersistentStoreCoordinator *)resetPersistentStore {
 NSError *error;

 [managedObjectContext lock];

 // FIXME: dirty. If there are many stores...
 NSPersistentStore *store = [[persistentStoreCoordinator persistentStores] objectAtIndex:0];

 if (![persistentStoreCoordinator removePersistentStore:store error:&error]) {
  NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
  abort();
    }  

 // Delete file
 if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
  NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
  abort();
    } 

 // Delete the reference to non-existing store
 [persistentStoreCoordinator release];
 persistentStoreCoordinator = nil;

 NSPersistentStoreCoordinator *r = [self persistentStoreCoordinator];
 [managedObjectContext unlock];

 return r;
}

Then in my view I do (in another thread since I am using MBProgressHUD:

PatrimoineAppDelegate *appDelegate = (PatrimoineAppDelegate *)[[UIApplication sharedApplication] delegate]; 
// Delete everything
[appDelegate resetPersistentStore];

And I get an EXC_BAD_ACESS...

I do not know CoreData or multithreading very well, maybe I'm doing an evident error...

+1  A: 

If your goal is to empty the data store and reload it with new information, you may be better off using NSManagedObjectContext's reset and then loading in new data.

From NSManagedObjectContext's documentation

A context always has a “parent” persistent store coordinator which provides the model and dispatches requests to the various persistent stores containing the data. Without a coordinator, a context is not fully functional. The context’s coordinator provides the managed object model and handles persistency. All objects fetched from an external store are registered in a context together with a global identifier (an instance of NSManagedObjectID) that’s used to uniquely identify each object to the external store.

Removing the persistent store and using the managed object context associated with the store is probably the cause of the error.

Giao
More information about my solution below.
charlax
Just to be sure to understand: my data are already saved in the persistent store. A simple `[moc reset]` (followed by a save) will wipe everything in the sqlite file too?
charlax
A: 

Here is the solution. There may be some more elegant options (lock...) but this one works.

/**
 * Will remove the persistent store
 */
- (NSPersistentStoreCoordinator *)resetPersistentStore {
    NSError *error = nil;

    if ([persistentStoreCoordinator persistentStores] == nil)
        return [self persistentStoreCoordinator];

    [managedObjectContext reset];
    [managedObjectContext lock];

    // FIXME: dirty. If there are many stores...
    NSPersistentStore *store = [[persistentStoreCoordinator persistentStores] lastObject];

    if (![persistentStoreCoordinator removePersistentStore:store error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }  

    // Delete file
    if ([[NSFileManager defaultManager] fileExistsAtPath:store.URL.path]) {
        if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        } 
    }

    // Delete the reference to non-existing store
    [persistentStoreCoordinator release];
    persistentStoreCoordinator = nil;

    NSPersistentStoreCoordinator *r = [self persistentStoreCoordinator];
    [managedObjectContext unlock];

    return r;
}
charlax
That is terribly ugly and overcomplicated. Why are you not just rebuilding the entire Core Data stack and handing off the new context to your view controller? Swapping out the PSC under the `NSManagedObjectContext` can be very risky. Secondarily you should never hit a `NSManagedObjectContext` from multiple threads, the stack is designed so that you create one context per thread for a reason; the context handles locking *correctly*. You are better off either rebuilding the entire stack or actually deleting the objects from your store and then reloading it as opposed to deleting the file.
Marcus S. Zarra
Thank you Marcus, I'm not sure to hear you ; how would you do to wipe out all data in the DB, in broad terms? What does CoreData stack imply?Let's say there is already some data in the persistent store, I would like to delete everything and then import new data. If I understand Apple's doc, reseting the MOC does not change anything in the persistent store, am I right?
charlax