ios - NSManagedObjectContext 没有正确保存到 SQLite

标签 ios objective-c sqlite core-data

所以,

我在使用 Core Data 和尝试将数据正确保存到 SQLite 数据库时遇到问题。

我有两个“应用程序”:一个用于加载 SQLite 数据(我们称之为“Loader.App”),另一个用于显示数据(“Display.App”)。加载程序只是基于 Web 的 CMS 和需要 SQLite 数据库(同样由 Core Data 加载)的应用程序之间的便利“桥梁”。

当我在 Loader.App 中保存上下文时,它会将数据保存到 SQLite 文件中。我可以在 SQLite 阅读器(如 Base.App)中打开此文件,它会显示所有数据。 问题是:当我将该 SQLite 文件带到 Display.App 时,它会将文件复制到文档目录中,但其中没有任何数据。但是,它确实具有所有正确的表 - 就像加载数据之前的 SQLite 文件一样。

奇怪的是,如果我在阅读器 (Base.App) 中打开 SQLite DB 文件并对数据库进行 VACUUM,它会完美地加载到 Display.App 中。根据 Python 文件 io 的经验,我知道如果你没有正确关闭文件,数据就不会从 io 缓冲区写入文件。显然,数据正在写入 SQLite 文件(因此我可以用阅读器 (Base.App) 打开它)。但这让我想知道是否有我没有调用的文件关闭方法?

基本上...

方法一:

  1. 运行 Loader.App
  2. 将 MyAppDB.sqlite 复制到 Display.App
  3. 运行 Display.App

结果:MyAppDB.sqlite 中没有数据

方法二:

  1. 运行 Loader.App
  2. 用阅读器(Base.App)打开MyAppDB.sqlite
  3. 真空
  4. 将 MyAppDB.sqlite 复制到 Display.App
  5. 运行 Display.App

结果:MyAppDB.sqlite 包含数据,我们很高兴。

这是我的 Loader.App 的精简版:

    int main(int argc, const char * argv[])
    {

        @autoreleasepool {
            // Create the managed object context
            NSManagedObjectContext *context = managedObjectContext();

            // Custom code here...
            importDataEntriesFromJSON(context);

            // Save the managed object context
            NSError *error = nil;
            if (![context save:&error]) {
                NSLog(@"Error while saving %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");
                exit(1);
            }
        }
        return 0;
    }

    static NSManagedObjectModel *managedObjectModel() {
        static NSManagedObjectModel *model = nil;
        if (model != nil) {
            return model;
        }

        NSString *path = @"MyAppDB";
        path = [path stringByDeletingPathExtension];
        NSURL *modelURL = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:@"mom"]];
        model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

        return model;
    }


    static NSManagedObjectContext *managedObjectContext() {
        static NSManagedObjectContext *context = nil;
        if (context != nil) {
            return context;
        }

        @autoreleasepool {
            context = [[NSManagedObjectContext alloc] init];

            NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel()];
            [context setPersistentStoreCoordinator:coordinator];

            NSString *STORE_TYPE = NSSQLiteStoreType;

            NSString *path = @"MyAppDB";
            path = [path stringByDeletingPathExtension];
            NSURL *url = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:@"sqlite"]];

            // Clear old SQLite
            NSFileManager *manager = [NSFileManager defaultManager];
            NSError *error;
            [manager removeItemAtURL:url error:&error];

            //NSError *error;
            NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE configuration:nil URL:url options:nil error:&error];

            if (newStore == nil) {
                NSLog(@"Store Configuration Failure %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");
            }
        }
        return context;
    }

    void importDataEntriesFromJSON( NSManagedObjectContext *context ) {
        NSError* err = nil;
        NSString* dataPath = [[NSBundle mainBundle] pathForResource:@"data_entries" ofType:@"json"];
        NSArray* data_entries = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
                                                        options:kNilOptions
                                                          error:&err];
        NSLog(@"Imported %lu data_entries from JSON", (unsigned long)[data_entries count]);

        [data_entries enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            DBDataEntry *dataEntry = [NSEntityDescription
                              insertNewObjectForEntityForName:@"DBDataEntry"
                              inManagedObjectContext:context];

            dataEntry.data_entry_id       = [NSNumber numberWithInt:[[obj objectForKey:@"data_entry_id"] integerValue]];
            dataEntry.data_entry_keywords = [obj objectForKey:@"data_entry_keywords"];
            dataEntry.data_entry_name     = [obj objectForKey:@"data_entry_name"];

            NSError *error;
            if (![context save:&error]) {
                NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
            }
        }];

        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"DBDataEntry"
                                          inManagedObjectContext:context];
        [fetchRequest setEntity:entity];

        NSError *error;
        NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
        for( DBDataEntry *dataEntry in fetchedObjects ) {
            //NSLog(@"data_entry_id: %@", dataEntry.data_entry_id);
            //NSLog(@"data_entry_keywords: %@", dataEntry.data_entry_keywords);
            //NSLog(@"data_entry_name: %@", dataEntry.data_entry_name);
            NSLog(@"data_entry_id: %@ name: %@", dataEntry.data_entry_id, dataEntry.data_entry_name);
        }
    }

感谢您的帮助! :)

最佳答案

最可能的原因是您正在复制 SQLite 文件本身,而不是它的日志文件。在 iOS 7 上,Core Data 通常在 WAL(预写日志记录)模式下使用 SQLite。这意味着除了 MyAppDB.sqlite 之外,还有名为 MyAppDB.sqlite-walMyAppDB.sqlite-shm 的文件。这些文件至关重要。如果您只复制 SQLite 文件而不复制日志,您将丢失数据(如您所见)。

当您在 Base.app 和 vacuum 中打开 SQLite 文件时,日志文件中的所有更改都会滚动到主 SQLite 文件本身。您正在执行一个额外的步骤,无需复制日志文件。

您有几个不同的选择:

  • 最简单的方法是复制所有文件。问题已解决。
  • 另一种选择是更改加载器应用中的日志模式,以避免复制更多文件的需要。您可以通过在添加持久存储时传递一个额外的选项来做到这一点:

    NSDictionary *options = @{ NSSQLitePragmasOption :  @{ @"journal_mode": @"DELETE" } };
    

在调用 addPersistentStoreWithType:configuration:URL:options:error: 时使用它。

关于ios - NSManagedObjectContext 没有正确保存到 SQLite,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20687547/

相关文章:

ios - 如何将数据保护与核心数据一起使用?

sqlite - 如何打开位于 sqlite3 中特定文件夹中的数据库文件?

ios - 如何在iOS的Google TTS API中更改语言?

ios - 如何使用CoreLocation获取多个iBeacon?

iphone - 从一个 View Controller 移动到另一个 View Controller 时,基于 View 的应用程序从左到右过渡

ios - 在 iOS 上使用 Whatsapp url scheme 调用

ios - Xcode Objective C 宏和build设置,切换开发、暂存和生产——一个完整的例子?

ios - 自定义键盘返回标题设置 IOS8

ios - 以编程方式在 iPhone 上调用紧急电话

java - Android SQLiteDatabase 查询忽略空格