Как заставить coredata перестроить модель базы данных sqlite? - PullRequest
11 голосов
/ 01 марта 2010

В моем приложении мне иногда нужно пересобрать и снова заполнить файл базы данных. База данных SQLite создается и управляется стеком CoreData.

Я пытаюсь удалить файл, а затем просто воссоздать объект persistentStoreCoordinator.

Работает под симулятором, а не на устройстве, где я получаю такую ​​ошибку:

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

Я никак не могу найти причину этого. Это указывает на две различные проблемы - ошибка 256 Какао указывает, что файл не существует или не читается. Но файл IS , созданный после создания persistenStoreCoordinator, хотя и пуст, но после выполнения некоторых запросов он исчезает.

Второе сообщение, указывающее на попытку создания уже существующей таблицы, довольно странно в этом случае.

Я совершенно сбит с толку и не могу понять, что здесь происходит. Мой код выглядит так:

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

После выполнения этого кода файл БД существует, но он пуст. Когда затем выполняется первый запрос (и все последующие), он выдает ошибку, указанную выше, и файл исчезает.

Кто-нибудь знает, что с ним не так?

Большое спасибо, что указали мне правильный путь!

Ответы [ 3 ]

18 голосов
/ 01 марта 2010

Стек основных данных не любит, когда вы удаляете файл под ним. Если вы хотите удалить файл, вы должны разрушить стек, удалить файл и затем восстановить его. Это устранит проблему.

Часть проблемы заключается в том, что в стеке хранится кэш данных, находящихся в файле. Когда вы удаляете файл, у вас нет способа очистить этот кеш, а затем вы переводите базовые данные в неизвестное и нестабильное состояние.

Вы можете указать NSPersistentStoreCoordinator, что вы удаляете файл с вызовом -removePersistentStore:error:, а затем добавляете новый магазин с вызовом на -addPersistentStoreWithType:configuration:URL:options:error:. Я делаю это в настоящее время в ZSync, и он работает просто отлично.

8 голосов
/ 21 мая 2010

Я использую следующий метод -resetApplicationModel в моем делегате приложения, и он прекрасно работает для меня.

Вам может не потребоваться пользовательское значение по умолчанию kApplicationIsFirstTimeRunKey, но я использую его, чтобы проверить, следует ли заполнять хранилище базовых данных параметрами по умолчанию в пользовательском методе, называемом -setupModelDefaults, который я также вызываю из -applicationDidFinishLaunching:, если первый время выполнения: 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;
}
1 голос
/ 01 марта 2010

Вы можете сохранить «чистую» копию базы данных sqlite как часть пакета приложения, а затем просто скопировать версию в каталог документов, когда захотите обновить базу данных.

Вот некоторый код из приложения, которое делает нечто похожее (хотя эта версия не будет копировать и существующие БД):

// 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];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...