私は iOS 7 用の既存のアプリを更新中ですが、オブジェクトを保存する Core Data でいくつかの問題が発生しています。ストレージに Core Data を使用する、かなり単純なマスター/ディテール スタイルのデータ入力アプリです。
新しいレコードを追加するとき、2 番目の (一時的な) 管理オブジェクト コンテキストを使用して、レコードが保存される前にレコードがリストに表示されないようにします。レコードを追加して保存すると、期待どおりにリストに表示されます。ただし、アプリを終了して (バックグラウンドでは実行されません)、再起動すると、レコードは存在しなくなります。レコードはデータベースに存在します (とにかく SQLite Manager Firefox プラグインを使用して表示されます) が、アプリには表示されません。
新しいプロジェクトを作成するときに Xcode が生成するコードを使用して、これを再現することができました。新しいマスター/ディテール アプリケーションを作成し、[コア データを使用] ボックスにチェックを入れてサンプル コードを取得し、次の変更を加えました。
以下をMasterViewController.mに追加します
-(void)save:(NSManagedObjectContext*)context
{
if (context != [self.fetchedResultsController managedObjectContext])
{
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:@selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:context];
}
NSError *error;
if (![context save:&error])
{
abort();
}
if (context != [self.fetchedResultsController managedObjectContext])
{
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:context];
}
}
- (void)addControllerContextDidSave:(NSNotification*)saveNotification
{
[[self.fetchedResultsController managedObjectContext] mergeChangesFromContextDidSaveNotification:saveNotification];
}
insertNewObject
追加用の新しい一時コンテキストを作成するには、insertNewObject で指定されたものを次のように置き換えます。
- (void)insertNewObject:(id)sender
{
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = [[self.fetchedResultsController managedObjectContext] persistentStoreCoordinator];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
// Save the context.
[self save:context];
}
また、アプリをバックグラウンドで実行しないように設定しました。
これを iOS 6 に対して実行すると、期待どおりに動作します。つまり、[追加] をタップすると新しいレコードが表示され、アプリを終了して再起動すると、レコードがまだ存在します。
ただし、iOS 7 に対して同じコードを実行すると、正しく動作しません。[追加] をタップすると、新しいレコードが表示されますが、終了してアプリを再起動すると、レコードが表示されません。ただし、前述のように、データベースには存在します。
興味深いことに、SQLite データベースのジャーナリング モードの変更に何らかの形で関連している可能性があることを発見しました。への呼び出しに次のオプションを追加すると、addPersistentStoreWithType
iOS 7 で実行される期待される動作が得られます
NSDictionary *options = @{ NSSQLitePragmasOption : @{@"journal_mode" : @"DELETE"} };
では、質問へ (そして、ここまで読んでくれてありがとう!)
- 他の誰かがこの動作を見たことがありますか (または、上記の説明に基づいて再現できる人はいますか)?
- iOS 7 より前は幸運だった一時的なコンテキストの使用方法に問題があるのでしょうか、それとも iOS 7 の Core Data フレームワークの問題のように見えますか?
乾杯
ニール
編集1:
メイン MOC の保存に関する Wain の質問への回答では、データが既に保存されているため、これは実際には必要ないという印象を受けました。マージは、既に保存されている変更を一時コンテキストからメイン コンテキストに更新するだけです。つまり、テスト コードには次のメソッドが含まれておりsaveContext
、シャットダウン時に呼び出されますが、 [managedObjectContext hasChanges]
false を返すため、この時点では実際には何も実行されません。
-(void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self saveContext];
}
-(void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil)
{
if ([managedObjectContext hasChanges])
{
if (![managedObjectContext save:&error])
{
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
}