2010-03-01 6 views
10

Dans mon application, j'ai parfois besoin de reconstruire et repeupler le fichier de base de données. La base de données SQLite est créée et gérée par la pile CoreData. Ce que j'essaie de faire est de supprimer le fichier et de recréer simplement l'objet persistentStoreCoordinator.Comment forcer Coredata à reconstruire le modèle de base de données sqlite?

Il fonctionne sous simulateur mais pas sur l'appareil, où je reçois une telle erreur:

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

Je ne peux pas trouver la cause de ce en aucune façon. Il indique deux problèmes différents - L'erreur Cocoa 256 indique que le fichier n'existe pas ou n'est pas lisible. Mais le fichier IS est créé après la création de persistenStoreCoordinator, bien qu'il soit vide, mais après l'exécution de certaines requêtes, il disparaît.

Deuxième message indiquant tentative de créer alredy table existante est assez étrange dans ce cas.

Je suis assez confus et je n'arrive pas à comprendre ce qui se passe ici. Mon code ressemble à ceci:

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

Après l'exécution de ce code, le fichier DB existe mais est vide. Lorsque la première requête (et toutes les suivantes) est exécutée, cela me donne l'erreur ci-dessus et le fichier disparaît.

Est-ce que quelqu'un a une idée de ce qui ne va pas?

Un grand merci de m'avoir indiqué la bonne façon!

Répondre

18

La pile de données de base ne vous aime pas supprimer le fichier en dessous. Si vous souhaitez supprimer le fichier, vous devez supprimer la pile, supprimer le fichier et reconstruire la pile. Cela permettra d'éliminer le problème.

Une partie du problème est que la pile conserve un cache des données contenues dans le fichier. Lorsque vous supprimez le fichier, vous ne pouvez plus effacer ce cache et vous placez alors les données de base dans un état inconnu et instable.

Vous pouvez essayer de dire le NSPersistentStoreCoordinator vous supprimez le fichier avec un appel à -removePersistentStore:error:, puis en ajoutant le nouveau magasin avec un appel à -addPersistentStoreWithType:configuration:URL:options:error:. Je le fais actuellement dans ZSync et ça marche très bien.

+0

Merci pour un conseil, c'est intéressant et je vais l'essayer.Mais n'est-ce pas la même chose lorsque toutes les instances de NSPersistentStoreCoordinator sont publiées juste avant la suppression du fichier? C'est ce qui arrive juste avant d'appeler ma méthode ci-dessus. Je suppose qu'il supprimera et libérera toutes les structures de données suivantes. Pouvez-vous expliquer pourquoi il est important de supprimer explicitement le magasin persistant du coordinateur avant de supprimer le fichier lui-même? – Burt

+0

J'ai donc essayé votre solution et cela fonctionne! Un grand merci pour ça! Cependant je me demande toujours où est la différence entre supprimer explicitement le magasin persistant et simplement libérer le coordinateur. – Burt

+1

Si vous définissez 'NSPersistentStoreCoordinator' à nil dans le cadre de 'NSManagedObjectContext' et le libérez, alors vous obtiendrez probablement la même chose, mais c'est beaucoup plus lourd que d'indiquer à' NSPersistentStoreCoordinator' de supprimer le magasin. –

1

Vous pouvez conserver une copie "propre" de votre base de données sqlite dans le cadre de l'ensemble d'applications, puis copier simplement la version dans le répertoire de documents chaque fois que vous souhaitez actualiser la base de données.

est ici un code d'une application qui fait quelque chose de similaire (bien que cette version ne sera pas copier et db existant):

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

J'utilise la méthode suivante -resetApplicationModel dans mon délégué de l'application et cela fonctionne très bien pour moi.

Vous ne pouvez pas besoin de l'utilisateur par défaut kApplicationIsFirstTimeRunKey, mais je l'utiliser pour vérifier si pour alimenter le magasin de base de données avec les paramètres par défaut dans une méthode personnalisée appelée -setupModelDefaults, que j'appelle aussi de -applicationDidFinishLaunching: si le premier indicateur de temps d'exécution est 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

Salut @Alex Reynolds, je veux réinitialiser toutes les données dans mon coreData, je veux savoir, ce que la fonction [setupModeDefaults] faire.?.Aussi s'il vous plaît jeter un oeil à ce http://stackoverflow.com/questions/14646595/ how-to-clear-reset-tout-coredata-dans-un-à-plusieurs-realationship – Ranjit

+0

C'est juste une méthode qui repeuple le magasin de données de base avec des valeurs par défaut. Vous écririez votre propre méthode pour remettre les valeurs par défaut. –

+0

merci de répondre, j'ai essayé votre code, mais il ne fonctionne pas.Est-ce que vous s'il vous plaît aidez-moi – Ranjit

Questions connexes