2010-03-01 1 views
10

Nella mia app a volte ho bisogno di ricostruire e ripopolare il file di database. Il databse SQLite è creato e gestito dallo stack CoreData.Come forzare il coredata per ricostruire il modello di database sqlite?

Quello che sto cercando di fare è rilasciare il file e quindi semplicemente ricreare l'oggetto persistentStoreCoordinator.

Opera sotto simulatore, ma non sul dispositivo, dove sto ottenendo un errore del genere:

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'; 

non riesco a trovare la causa di questo in alcun modo. Indica due diversi problemi: l'errore 256 di Cocoa indica che il file non esiste o non è leggibile. Ma il file IS creato dopo aver creato persistenStoreCoordinator, anche se è vuoto, ma dopo aver eseguito alcune query scompare.

Secondo messaggio che indica il tentativo di creare tabella esistente già esistente in questo caso è abbastanza strano.

Sono abbastanza confuso e non riesco a capire cosa sta succedendo qui. Il mio codice è simile al seguente:

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]); 

Dopo che questo codice è stato eseguito, il file DB esiste ma è vuoto. Quando viene eseguita la prima query (e tutte le successive), mi viene restituito l'errore sopra e il file scompare.

Qualcuno ha un'idea di cosa c'è che non va?

Grazie mille per avermi indicato nel modo giusto!

risposta

18

Lo stack di Core Data non ti piace la rimozione del file di sotto di essa. Se si desidera eliminare il file, è necessario abbattere lo stack, eliminare il file e quindi ricostruire lo stack. Questo eliminerà il problema.

Parte del problema è che lo stack conserva una cache dei dati presenti nel file. Quando rimuovi il file non hai un modo per svuotare quella cache e quindi stai mettendo i dati di base in uno stato sconosciuto e instabile.

È possibile provare a dire al numero NSPersistentStoreCoordinator che si sta rimuovendo il file con una chiamata a -removePersistentStore:error: e quindi aggiungere il nuovo negozio con una chiamata a -addPersistentStoreWithType:configuration:URL:options:error:. Lo sto facendo attualmente in ZSync e funziona perfettamente.

+0

Grazie per una punta, interessa e ho intenzione di provarlo.Ma non è lo stesso quando tutte le istanze di NSPersistentStoreCoordinator vengono rilasciate appena prima che il file venga rimosso? Questo è ciò che accade prima di chiamare il mio metodo sopra. Suppongo che rimuoverà e libererà anche tutte le strutture dati successive. Puoi spiegare perché è importante rimuovere esplicitamente lo store persistente dal coordinatore prima di rimuovere il file stesso? – Burt

+0

Quindi ho provato la soluzione e funziona! Grazie mille per questo! Tuttavia mi sto ancora chiedendo dove sia la differenza tra rimuovere esplicitamente lo store persistente e rilasciare semplicemente il coordinatore. – Burt

+1

Se si imposta 'NSPersistentStoreCoordinator' su nil all'interno di' NSManagedObjectContext' e lo si rilascia, allora probabilmente si otterrà la stessa cosa ma questo è molto più pesante del semplice istruire 'NSPersistentStoreCoordinator' per rimuovere lo store. –

1

È possibile mantenere una copia "pulita" del database sqlite come parte del pacchetto di applicazioni, quindi copiare semplicemente la versione nella directory dei documenti ogni volta che si desidera aggiornare il database.

Ecco qualche codice da un app che fa qualcosa di simile (anche se questa versione non sarà copiare e db esistente):

// 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

Io uso il seguente metodo -resetApplicationModel nel delegato della mia app e funziona correttamente per me.

Potrebbe non essere necessario il default kApplicationIsFirstTimeRunKey utente, ma lo uso per verificare se per popolare il negozio Core Data con le impostazioni predefinite in un metodo personalizzato chiamato -setupModelDefaults, che chiamo anche dal -applicationDidFinishLaunching: se il flag di esecuzione per la prima volta è 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

Ciao @Alex Reynolds, voglio reimpostare tutti i dati nel mio coreData, voglio sapere, cosa fa la funzione [setupModeDefaults]. Anche per favore dai un'occhiata a questo http://stackoverflow.com/questions/14646595/ how-to-clear-reset-all-coredata-in-one-to-many-realationship – Ranjit

+0

È solo un metodo che ripopola l'archivio dei dati principali con valori predefiniti. Scriveresti il ​​tuo metodo per reinserire i valori predefiniti. –

+0

grazie per aver risposto, ho provato il tuo codice, ma non funziona. Ti prego di aiutarmi – Ranjit