【问题标题】:How to force coredata to rebuild sqlite database model?如何强制coredata重建sqlite数据库模型?
【发布时间】:2011-01-22 08:32:39
【问题描述】:

在我的应用程序中,我有时需要重建和重新填充数据库文件。 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';

我无法以任何方式找到造成这种情况的原因。它表示两个不同的问题 - Cocoa 错误 256 表示文件不存在或不可读。但是创建persistenStoreCoordinator后创建的文件IS,虽然是空的,但是在执行了一些查询之后就消失了。

在这种情况下,指示尝试创建已经存在的表的第二条消息很奇怪。

我很困惑,无法理解这里发生了什么。我的代码如下所示:

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

执行此代码后,数据库文件存在但为空。然后执行第一个查询(以及所有后续查询)时,它给了我上面的错误并且文件消失了。

有人知道它有什么问题吗?

非常感谢您指点我正确的方式!

【问题讨论】:

    标签: iphone core-data sqlite


    【解决方案1】:

    我在我的应用程序委托中使用以下方法-resetApplicationModel,它对我来说很好。

    您可能不需要kApplicationIsFirstTimeRunKey 用户默认值,但我使用它来测试是否在名为-setupModelDefaults 的自定义方法中使用默认设置填充Core Data 存储,如果第一个,我也从-applicationDidFinishLaunching: 调用-time 运行标志是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;
    }
    

    【讨论】:

    • 嗨@Alex Reynolds,我想重置我的coreData中的所有数据,我想知道[setupModeDefaults]函数是做什么的。?也请看看这个stackoverflow.com/questions/14646595/…跨度>
    • 这只是一种使用默认值重新填充核心数据存储的方法。您可以编写自己的方法来恢复默认值。
    • 感谢您的回复,我尝试了您的代码,但它不起作用。请您帮帮我
    • 我不想将它设置为任何默认值,所以,我决定不使用该功能,所以我只调用 [self managedObjectContext]。所以当我打开我的 ViewController,我的 NSFetchResultsController,仍然有数据,因此我收到错误。
    【解决方案2】:

    Core Data 堆栈不喜欢你删除它下面的文件。如果要删除文件,则应拆除堆栈,删除文件,然后重建堆栈。这将消除这个问题。

    部分问题是堆栈保留了文件中数据的缓存。当您删除文件时,您无法清除缓存,然后您将 Core Data 置于未知且不稳定的状态。

    您可以尝试通过调用-removePersistentStore:error: 告诉NSPersistentStoreCoordinator 您正在删除文件,然后通过调用-addPersistentStoreWithType:configuration:URL:options:error: 添加新存储。我目前在 ZSync 中这样做,效果很好。

    【讨论】:

    • 感谢您的提示,这很有趣,我将尝试一下。但是,当 NSPersistentStoreCoordinator 的所有实例都在文件被删除之前被释放时,情况不一样吗?这就是在调用我上面的方法之前发生的事情。我想它也会删除并释放所有后续数据结构。你能解释一下为什么在删除文件本身之前从协调器中显式删除持久存储很重要吗?
    • 所以我已经尝试了您的解决方案并且它有效!非常感谢!但是我仍然想知道显式删除持久存储和简单地释放协调器之间的区别在哪里。
    • 如果您在NSManagedObjectContext 中将NSPersistentStoreCoordinator 设置为nil 并释放它,那么您可能会完成同样的事情,但这比仅仅指示NSPersistentStoreCoordinator 删除存储要重得多.
    • 那么,当要求NSPersistentStoreCoordinator 删除商店时,我如何确定要传递的NSPersistenStore 是什么?
    • 您可以查看配置是字符串,路径等。或者您可以在构造NSPersistentStoreCoordinator时保留对它的引用。
    【解决方案3】:

    您可以保留一个“干净”的 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];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-07
      • 2020-04-30
      • 2011-01-22
      • 2011-06-10
      • 2022-01-26
      • 2015-05-06
      • 2011-10-23
      • 2016-07-03
      相关资源
      最近更新 更多