【发布时间】:2012-05-04 06:40:02
【问题描述】:
结论
我认为问题已解决。
看起来问题与方法无关,但 XCode 在构建之间没有正确清理项目。
看起来在所有这些测试之后,正在使用的 sqlite 文件仍然是第一个没有被索引的文件......
当心 XCode 4.3.2,除了清理不清理或将文件添加到项目中不会自动添加到捆绑资源中的问题外,我什么都没有...
感谢不同的答案..
更新 3
由于我邀请任何人尝试相同的步骤以查看他们是否获得相同的结果,让我详细说明我做了什么:
我从空白项目开始
我用一个实体、3 个属性(2 个字符串、1 个浮点数)定义了一个数据模型
第一个字符串被索引
在没有finishLaunchingWithOptions,我打电话:
[self performSelectorInBackground:@selector(populateDB) withObject:nil];
populateDb 的代码如下:
-(void)populateDB{
NSLog(@"start");
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
NSManagedObjectContext *context;
if (coordinator != nil) {
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coordinator];
}
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"input" ofType:@"txt"];
if (filePath) {
NSString * myText = [[NSString alloc]
initWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:nil];
if (myText) {
__block int count = 0;
[myText enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
line=[line stringByReplacingOccurrencesOfString:@"\t" withString:@" "];
NSArray *lineComponents=[line componentsSeparatedByString:@" "];
if(lineComponents){
if([lineComponents count]==3){
float f=[[lineComponents objectAtIndex:0] floatValue];
NSNumber *number=[NSNumber numberWithFloat:f];
NSString *string1=[lineComponents objectAtIndex:1];
NSString *string2=[lineComponents objectAtIndex:2];
NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:@"Bigram" inManagedObjectContext:context];
[object setValue:number forKey:@"number"];
[object setValue:string1 forKey:@"string1"];
[object setValue:string2 forKey:@"string2"];
NSError *error;
count++;
if(count>=1000){
if (![context save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
count=0;
}
}
}
}];
NSLog(@"done importing");
NSError *error;
if (![context save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
}
}
NSLog(@"end");
}
其他一切都是默认核心数据代码,没有添加任何内容。
我在模拟器中运行它。
我去~/Library/Application Support/iPhone Simulator/5.1/Applications//Documents
有生成的sqlite文件
我把它复制到我的包中
我注释掉对 populateDb 的调用
我编辑 persistentStoreCoordinator 以在第一次运行时将 sqlite 文件从包复制到文档
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
@synchronized (self)
{
if (__persistentStoreCoordinator != nil)
return __persistentStoreCoordinator;
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"myProject" ofType:@"sqlite"];
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: @"myProject.sqlite"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(@"Copied starting data to %@", storePath);
else
NSLog(@"Error copying default DB to %@ (%@)", storePath, error);
}
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
}
我从模拟器中删除了应用程序,我检查了 ~/Library/Application Support/iPhone Simulator/5.1/Applications/ 现在是否已删除
我重新构建并再次启动
正如预期的那样,sqlite 文件被复制到 ~/Library/Application Support/iPhone Simulator/5.1/Applications//Documents
但文件的大小明显小于捆绑包中的大小!
另外,使用这样的谓词进行简单查询 predicate = [NSPredicate predicateWithFormat:@"string1 == %@", string1];清楚地表明 string1 不再被索引
之后,我创建了一个新版本的数据模型,并进行了无意义的更新,只是为了强制进行轻量级迁移
如果在模拟器上运行,迁移需要几秒钟,数据库大小会翻倍,现在返回相同的查询只需不到一秒钟而不是几分钟。
这将解决我的问题,强制迁移,但相同的迁移在 iPad 上需要 3 分钟并发生在前台。
所以这就是我现在所处的位置,对我来说最好的解决方案仍然是防止索引被删除,在启动时任何其他导入解决方案都需要太多时间。
如果您需要更多说明,请告诉我...
更新 2
所以到目前为止我得到的最好的结果是使用具有类似数据模型的快速工具生成的 sqlite 文件为核心数据数据库播种,但在生成 sqlite 文件时没有设置索引。然后,我将这个 sqlite 文件导入核心数据应用程序并设置索引,并允许进行轻量级迁移。对于新 iPad 上的 200 万条记录,此迁移剧照需要 3 分钟。最终的应用程序应该有这个数量的 5 倍的记录,所以我们仍然在寻找一个很长的处理时间。
如果我走那条路,新的问题将是:可以在后台执行轻量级迁移吗?
更新
我的问题不是如何创建一个工具来填充 Core Data 数据库,然后将 sqlite 文件导入我的应用程序。
我知道如何做到这一点,我已经做过无数次了。
但直到现在,我没有意识到这种方法可能会产生一些副作用:在我的情况下,以这种方式导入 sqlite 文件时,结果数据库中的索引属性显然会“未索引”。
如果您能够验证在此类传输后任何索引数据仍然被索引,我很想知道您如何进行,或者有效地播种此类数据库的最佳策略是什么。
原创
我有一个包含 4 列、字符串和浮点数的大型 CSV 文件(数百万行)。
这是针对 iOS 应用程序的。
我需要在第一次加载应用程序时将其加载到核心数据中。
在数据可用之前,该应用程序几乎无法运行,因此加载时间很重要,因为第一次使用的用户显然不希望应用程序在能够运行之前加载 20 分钟。
现在,我当前的代码在新 iPad 上需要 20 分钟来处理一个 200 万行的 csv 文件。
我正在使用后台上下文来不锁定 UI,并且每 1,000 条记录保存一次上下文
我的第一个想法是在模拟器上生成数据库,然后在首次启动时将其复制/粘贴到文档文件夹中,因为这是播种大型数据库的常见非官方方式。不幸的是,索引似乎无法在这样的传输中幸存下来,尽管数据库在几秒钟后就可以使用,但性能很糟糕,因为我的索引丢失了。我已经发布了一个关于索引的问题,但似乎没有一个好的答案。
所以我要找的是:
- 一种提高核心数据中数百万条记录加载性能的方法
- 如果数据库在首次启动时预加载并移动,这是一种保留索引的方法
- 处理这种情况的最佳实践。我不记得使用过任何需要我在第一次使用前等待 x 分钟的应用程序(但也许是 The Daily,那是一次糟糕的体验)。
- 任何让用户在不知不觉中等待的创造性方法:在浏览教程时进行后台导入等...
- 不使用核心数据?
- ...
【问题讨论】:
-
那么您是如何最终“清理”项目以使其正常工作的?
-
清理不起作用,但重新启动笔记本电脑、手动清理对文件的所有引用等似乎已经“解决”了问题。奇怪......虽然我也不得不删除轻量级迁移线以强制不迁移(因为这需要很多分钟)。总的来说,这不是我希望的干净的实现,但这很有效......直到第 2 版需要升级数据模型,然后我就有麻烦了