所以,
我在使用 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) 打开它)。但这让我想知道是否有我没有调用的文件关闭方法?
基本上...
方法一:
- 运行 Loader.App
- 将 MyAppDB.sqlite 复制到 Display.App
- 运行 Display.App
结果:MyAppDB.sqlite 中没有数据
方法二:
- 运行 Loader.App
- 用阅读器(Base.App)打开MyAppDB.sqlite
- 真空
- 将 MyAppDB.sqlite 复制到 Display.App
- 运行 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-wal
和 MyAppDB.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/