2010-03-01 6 views
10

En mi aplicación a veces necesito reconstruir y volver a llenar el archivo de la base de datos. SQLite databse es creado y administrado por la pila CoreData.¿Cómo forzar a coredata a reconstruir el modelo de base de datos sqlite?

Lo que intento hacer es soltar el archivo y luego simplemente recrear el objeto persistentStoreCoordinator.

funciona bajo el simulador, pero no en el dispositivo, donde Recibo un error de este tipo:

NSFilePath = "/var/mobile/Applications/936C6CC7-423A-46F4-ADC0-7184EAB0CADD/Documents/MYDB.sqlite"; 
NSUnderlyingException = I/O error for database at /var/mobile/Applications/936C6CC7-423A-46F4-ADC0-7184EAB0CADD/Documents/MYDB.sqlite. SQLite error code:1, 'table ZXXXX already exists'; 

no puedo encontrar la causa de esto de ninguna manera. Indica dos problemas diferentes: el error de cacao 256 indica que el archivo no existe o no es legible. Pero el archivo IS se creó después de crear persistenStoreCoordinator, aunque está vacío, pero después de ejecutar algunas consultas, desaparece.

El segundo mensaje que indica que se intentó crear una tabla existente es bastante extraño en ese caso.

Estoy bastante confundido y no entiendo qué está pasando aquí. Mi código tiene este aspecto:

NSString *path = [[WLLocalService dataStorePath] relativePath]; 
NSError *error = nil; 

WLLOG(@"About to remove file %@", path); 

[[NSFileManager defaultManager] removeItemAtPath: path error: &error]; 

if (error != nil) { 
WLLOG(@"Error removing the DB: %@", error); 
} 


[self persistentStoreCoordinator]; 

WLLOG(@"Rebuild DB result %d", [[NSFileManager defaultManager] fileExistsAtPath: path]); 

Después de ejecutar este código, el archivo DB existe pero está vacío. Cuando se ejecuta la primera consulta (y todas las siguientes), me da el error anterior y el archivo desaparece.

¿Alguien tiene una idea de lo que está mal con eso?

Muchas gracias por indicarme el camino correcto!

Respuesta

18

La pila de Datos Básicos no le gusta que la eliminación del archivo debajo de ella. Si desea eliminar el archivo, debe eliminar la pila, eliminar el archivo y luego reconstruir la pila. Eso eliminará el problema.

Parte del problema es que la pila mantiene un caché de los datos que se encuentran en el archivo. Cuando elimina el archivo, no tiene forma de borrar ese caché y, a continuación, coloca Core Data en un estado desconocido e inestable.

Puede intentar decirle al NSPersistentStoreCoordinator que está eliminando el archivo con una llamada al -removePersistentStore:error: y luego agregar la nueva tienda con una llamada al -addPersistentStoreWithType:configuration:URL:options:error:. Lo estoy haciendo actualmente en ZSync y funciona muy bien.

+0

Gracias por un consejo, es interesante y voy a intentarlo.¿Pero no es lo mismo cuando todas las instancias de NSPersistentStoreCoordinator se lanzan justo antes de que se elimine el archivo? Eso es lo que sucede justo antes de llamar a mi método anterior. Supongo que eliminará y liberará todas las estructuras de datos posteriores también. ¿Puedes explicar por qué es importante eliminar explícitamente la tienda persistente del coordinador antes de eliminar el archivo? – Burt

+0

¡Así que probé tu solución y funciona! ¡Muchas gracias por eso! Sin embargo, todavía me pregunto cuál es la diferencia entre eliminar explícitamente la tienda persistente y simplemente liberar al coordinador. – Burt

+1

Si configura 'NSPersistentStoreCoordinator' como nulo dentro del' NSManagedObjectContext' y lo libera, entonces probablemente logre lo mismo, pero eso es mucho más pesado que simplemente instruir al 'NSPersistentStoreCoordinator' para eliminar la tienda. –

1

Puede mantener una copia "limpia" de su base de datos sqlite como parte del paquete de la aplicación, luego simplemente copie la versión en el directorio de documentos cada vez que desee actualizar la base de datos.

Aquí hay un código de una aplicación que hace algo similar (aunque esta versión no copiará una y db existente):

// Check for the existence of the seed database 
// Get the path to the documents directory and append the databaseName 
NSString* databasePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: kDatabaseName]; 

NSFileManager* fileManager = [NSFileManager defaultManager]; 
if (![fileManager fileExistsAtPath: databasePath]) 
{ 
    NSString* databasePathFromApp = [[[NSBundle mainBundle] resourcePath] 
            stringByAppendingPathComponent: kDatabaseName]; 

    [fileManager copyItemAtPath: databasePathFromApp 
         toPath: databasePath 
          error: nil]; 
} 
[fileManager release]; 
8

Uso el siguiente método -resetApplicationModel en mi delegado de aplicación y funciona bien para mí.

puede que no necesite el defecto kApplicationIsFirstTimeRunKey usuario, pero lo uso para probar si se rellena el almacén central de datos con la configuración predeterminada en un método personalizado llamado -setupModelDefaults, que también llamo desde -applicationDidFinishLaunching: si la bandera de ejecución por primera vez es YES.

- (BOOL) resetApplicationModel { 

    // ---------------------- 
    // This method removes all traces of the Core Data store and then resets the application defaults 
    // ---------------------- 

    [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:kApplicationIsFirstTimeRunKey]; 
    NSLog(@"Turned ON the first-time run flag..."); 

    NSError *_error = nil; 
    NSURL *_storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"MyAppSQLStore.sqlite"]]; 
    NSPersistentStore *_store = [persistentStoreCoordinator persistentStoreForURL:_storeURL]; 

    // 
    // Remove the SQL store and the file associated with it 
    // 
    if ([persistentStoreCoordinator removePersistentStore:_store error:&_error]) { 
     [[NSFileManager defaultManager] removeItemAtPath:_storeURL.path error:&_error]; 
    } 

    if (_error) { 
     NSLog(@"Failed to remove persistent store: %@", [_error localizedDescription]); 
     NSArray *_detailedErrors = [[_error userInfo] objectForKey:NSDetailedErrorsKey]; 
     if (_detailedErrors != nil && [_detailedErrors count] > 0) { 
      for (NSError *_detailedError in _detailedErrors) { 
       NSLog(@" DetailedError: %@", [_detailedError userInfo]); 
      }     
     } 
     else { 
      NSLog(@" %@", [_error userInfo]); 
     } 
     return NO; 
    } 

    [persistentStoreCoordinator release], persistentStoreCoordinator = nil; 
    [managedObjectContext release], managedObjectContext = nil; 

    // 
    // Rebuild the application's managed object context 
    // 
    [self managedObjectContext]; 

    // 
    // Repopulate Core Data defaults 
    // 
    [self setupModelDefaults]; 

    return YES; 
} 
+0

Hola @Alex Reynolds, quiero restablecer todos los datos en mi coreData, quiero saber qué función [setupModeDefaults] hacer.?. También eche un vistazo a este http://stackoverflow.com/questions/14646595/ how-to-clear-reset-all-coredata-in-one-to-many-realationship – Ranjit

+0

Es solo un método que repobla el almacén de Core Data con los valores predeterminados. Deberías escribir tu propio método para volver a los valores predeterminados. –

+0

gracias por responder, probé tu código, pero no funciona. ¿Podrías por favor ayudarme? – Ranjit

Cuestiones relacionadas