結論
問題は解決したと思います。
問題は方法論とは何の関係もなかったようですが、XCodeはビルドの合間にプロジェクトを正しくクリーンアップしませんでした。
これらすべてのテストの後、使用されていたsqliteファイルはまだインデックス付けされていない最初のファイルだったようです......
XCode 4.3.2に注意してください、私はCleanがクリーニングされない、またはプロジェクトにファイルを追加しても、バンドルリソースに自動的に追加されません...
さまざまな回答をありがとうございます。
更新3
誰もが同じ手順を試して同じ結果が得られるかどうかを確認するように勧めているので、私が行ったことを詳しく説明します。
空白のプロジェクトから始めて
、1つのエンティティ、3つの属性(2つの文字列、1つのフロート)でデータモデルを定義しました。
最初の文字列はインデックス付けされて
います。didfinishLaunchingWithOptionsで、次のように呼び出しています。
[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 / ApplicationSupport/にコピーされます。 iPhoneシミュレーター/5.1/アプリケーション//ドキュメント
ただし、ファイルのサイズはバンドルよりも大幅に小さくなっています。
また、この述語= [NSPredicate predicateWithFormat:@ "string1 ==%@"、string1];のような述語を使用して単純なクエリを実行します。string1がインデックスに登録されていないことを明確に示しています
。その後、軽量の移行を強制するために、意味のない更新を含む新しいバージョンのデータモデルを作成します
。シミュレータで実行すると、移行に数秒かかり、データベースのサイズが2倍になります。同じクエリが返されるのに数分ではなく1秒もかからないようになりました。
これは私の問題を解決し、移行を強制しますが、同じ移行はiPadで3分かかり、フォアグラウンドで行われます。
ですから、私が今いるところです。私にとって最善の解決策は、インデックスが削除されないようにすることです。起動時に他のインポートソリューションは時間がかかりすぎるだけです。
さらに詳しい説明が必要な場合はお知らせください...
アップデート2
これまでのところ、私がこれまでに得た最良の結果は、同様のデータモデルを備えたクイックツールから生成されたsqliteファイルをコアデータデータベースにシードすることですが、sqliteファイルを生成するときにインデックスは設定されていません。次に、このsqliteファイルをインデックスが設定されたコアデータアプリにインポートし、軽量の移行を可能にします。新しいiPadでの200万件の記録の場合、この移行にはまだ3分かかります。最終的なアプリにはこの数の5倍のレコードが含まれているはずなので、まだ長い長い処理時間を検討しています。そのルートに行くと、新しい質問は次のようになります。バックグラウンドで軽量の移行を実行できますか?
更新
私の質問は、Core Dataデータベースにデータを取り込むためのツールを作成し、sqliteファイルをアプリにインポートする方法ではありません。
私はこれを行う方法を知っています、私はそれを数え切れないほどしました。
しかし今まで、そのようなメソッドが何らかの副作用をもたらす可能性があることに気づいていませんでした。私の場合、sqliteファイルをそのようにインポートすると、結果のデータベースのインデックス付き属性が明らかに「インデックスなし」になりました。
インデックス付けされたデータがそのような転送後もインデックス化されていることを確認できた場合は、どのように進めるか、またはそのようなデータベースを効率的にシードするための最良の戦略は何かを知りたいと思います。
オリジナル
4列、文字列、浮動小数点数の大きなCSVファイル(数百万行)があります。これはiOSアプリ用です。
アプリを初めてロードするときに、これをコアデータにロードする必要があります。
データが利用可能になるまでアプリはほとんど機能しないため、読み込み時間が重要です。初めてのユーザーは、アプリを実行できるようになるまでにアプリの読み込みに20分かかることを望んでいないことは明らかです。
現在、私の現在のコードは、新しいiPadで200万行のcsvファイルを処理するのに20分かかります。
UIをロックせず、1,000レコードごとにコンテキストを保存するためにバックグラウンドコンテキストを使用しています
私が最初に考えたのは、シミュレーターでデータベースを生成し、最初の起動時にそれをコピーしてドキュメントフォルダーに貼り付けることでした。これは、大規模なデータベースをシードする一般的な非公式の方法です。残念ながら、インデックスはそのような転送に耐えられないようです。データベースはほんの数秒で利用可能になりましたが、インデックスが失われたため、パフォーマンスはひどいものになりました。すでにインデックスについての質問を投稿しましたが、それに対する良い答えはないようです。
だから私が探しているのは、次のいずれかです。
- コアデータに数百万のレコードをロードする際のパフォーマンスを向上させる方法
- データベースが最初の起動時にプリロードされて移動された場合、インデックスを保持する方法
- この種のシナリオを処理するためのベストプラクティス。最初に使用する前にx分待つ必要のあるアプリを使用したことを覚えていません(ただし、おそらくThe Dailyであり、それはひどい経験でした)。
- ユーザーが気付かないうちに待たせるためのクリエイティブな方法:チュートリアル中のバックグラウンドインポートなど...
- コアデータを使用していませんか?
- ..。