Iphone iOS7上具有多个NSManagedObjectContext的数据丢失

Iphone iOS7上具有多个NSManagedObjectContext的数据丢失,iphone,ios,objective-c,sqlite,core-data,Iphone,Ios,Objective C,Sqlite,Core Data,我正在为iOS 7更新一个现有应用程序,我在核心数据保存对象方面遇到了一些问题。它是一款非常简单的主-细节式数据输入应用程序,使用核心数据进行存储 添加新记录时,我使用第二个(临时)托管对象上下文来防止记录在保存前出现在列表中。添加并保存记录后,它将按预期显示在列表中。但是,如果我退出应用程序(它不会在后台运行),然后重新启动它,记录将不再存在。该记录存在于数据库中(无论如何使用SQLite Manager Firefox插件都可以看到),但它不会显示在应用程序中 我使用Xcode在创建新项目时

我正在为iOS 7更新一个现有应用程序,我在核心数据保存对象方面遇到了一些问题。它是一款非常简单的主-细节式数据输入应用程序,使用核心数据进行存储

添加新记录时,我使用第二个(临时)托管对象上下文来防止记录在保存前出现在列表中。添加并保存记录后,它将按预期显示在列表中。但是,如果我退出应用程序(它不会在后台运行),然后重新启动它,记录将不再存在。该记录存在于数据库中(无论如何使用SQLite Manager Firefox插件都可以看到),但它不会显示在应用程序中

我使用Xcode在创建新项目时生成的代码成功地重现了这一点。我已经创建了一个新的master detail应用程序,并勾选Use Core Data框以获取示例代码,然后进行了以下更改:

- (void)addControllerContextDidSave:(NSNotification*)saveNotification
{
    [[self.fetchedResultsController managedObjectContext] mergeChangesFromContextDidSaveNotification:saveNotification];

    [self save:[self.fetchedResultsController managedObjectContext]];
}
将以下内容添加到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上运行此应用程序,它的行为与预期一致,即我点击Add并显示一条新记录,然后退出并重新启动应用程序,该记录仍然存在

但是,如果我在iOS7上运行相同的代码,它就不能正常工作。点击Add会显示新记录,但如果我退出并重新启动应用程序,则不会显示该记录。如上所述,它存在于数据库中

有趣的是,我发现它可能在某种程度上与SQLite数据库日志记录模式的改变有关。如果我在调用
addPersistentStoreWithType
中添加以下选项,我将获得在iOS 7上运行的预期行为

NSDictionary *options = @{ NSSQLitePragmasOption : @{@"journal_mode" : @"DELETE"} };
所以,对于这些问题(感谢您阅读到这里!)

  • 是否有其他人见过这种行为(或者有人能够根据上述描述重现这种行为)
  • 在iOS 7之前,我只是幸运地使用了一个临时上下文,我使用的方式是否有问题,或者这看起来像是iOS 7上的核心数据框架的问题
干杯

尼尔

编辑1:

在回答Wain关于保存主MOC的问题时,我的印象是,实际上没有必要这样做,因为数据已经保存,合并只是将已保存的更改从中的临时上下文更新为主上下文。也就是说,测试代码确实包含以下方法,并且在关机时调用
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();
            }
        }
    }
}

在合并更改后保存主上下文时,它似乎是固定的:

- (void)addControllerContextDidSave:(NSNotification*)saveNotification
{
    [[self.fetchedResultsController managedObjectContext] mergeChangesFromContextDidSaveNotification:saveNotification];

    [self save:[self.fetchedResultsController managedObjectContext]];
}

更新:此错误是由于在
NSFetchedResultsController
中使用缓存造成的。因此,数据并没有丢失,只是您的
NSFetchedResultsController不显示它。需要进一步调查,以找出为什么缓存在其MOC合并更改时没有更新,但没有保存。

为什么不在获取请求中使用
includesPendingChanges
?@Wain-我想这可能是一种可能性(我不确定为什么没有更新,可能在编写原始代码时没有意识到该参数)但我真的很想了解为什么我的应用程序在你如何(或者更确切地说,何时)保存持久性上下文方面能像预期的那样工作?@Wain-我已经添加到原始帖子中,我认为它应该按照你的方式工作(检查添加后磁盘上的文件是否更新)。将
journal_mode
设置为
DELETE
基本上重置为iOS 7之前的值。考虑用你的采样项目来提升雷达。有趣的是,这似乎解决了这个问题。我很想知道你是否知道为什么会这样,因为我认为没有必要这样做,而且奇怪的是,当使用不同的日志类型时,行为会发生变化。谢谢Arkadiusz。我已经向苹果公司提交了一份bug报告:15161551