在iOS7上使用多个NSManagedObjectContexts丢失数据

时间:2013-10-06 08:26:29

标签: iphone ios objective-c sqlite core-data

我正在更新iOS 7的现有应用程序,我一直遇到Core Data保存对象的一些问题。这是一个相当简单的主 - 细节风格数据输入应用程序,它使用Core Data进行存储。

添加新记录时,我使用第二个(临时)托管对象上下文来防止记录在保存记录之前出现在列表中。添加并保存记录后,它将按预期显示在列表中。但是,如果我退出应用程序(它不在后台运行)然后重新启动它,则记录不再存在。该记录存在于数据库中(无论如何都使用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:

在回答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();
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

在合并更改后保存主要上下文时似乎已修复:

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

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

更新:此错误是由NSFetchedResultsController中的缓存引起的。因此,数据不会丢失,只是您的NSFetchedResultsController无法显示。需要进一步调查,以找出缓存在MOC合并更改时未更新的原因,但未保存。