在 iOS 5 中使用 UIManagedDocument 和父/子上下文导入核心数据背景

Posted

技术标签:

【中文标题】在 iOS 5 中使用 UIManagedDocument 和父/子上下文导入核心数据背景【英文标题】:Core Data background importing using UIManagedDocument and parent/child context in iOS 5 【发布时间】:2012-04-07 20:16:47 【问题描述】:

我在 ios 5 中有一个目录应用程序,它使用带有NSFetchedResultsController 的表视图控制器从 XML 下载数据并在 UITableView 中显示它们。数据存储在核心数据UIManagedDocument。因为我不想在下载和导入数据时阻塞主队列,所以我创建了一个后台队列用于下载数据和新的子队列NSManagedObjectContextNSPrivateQueueConcurrencyType 用于以document.managedObjectContext 作为父级导入数据。当我完成导入数据时,我 -save: 在子上下文中发生更改,并且更改会传播到父上下文。浏览目录时,我会在需要时导入其他数据。在UIManagedDocument 自动保存之前,一切正常。

我已经用-com.apple.CoreData.SQLDebug 1打开核心数据SQL调试,看看文档什么时候自动保存。

document.managedObjectContext 中创建具有重复 ID 的文档自动保存对象后(我所有的实体 id 数据库都有唯一的 id 参数)。

我做错了什么?


我创建了一个简单的示例代码来重现该问题。 这是代码:http://dl.dropbox.com/u/20987346/ViewController.m 这是完整的 Xcode 项目:http://dl.dropbox.com/u/20987346/CoreDataTest.zip

下面是在后台进行导入的方法。

- (void)backgroundImport

    static int counter;

    NSManagedObjectContext *backgroundContext;
    backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    backgroundContext.parentContext = self.document.managedObjectContext;

    [backgroundContext performBlock:^
        NSManagedObject *entity;

        for (int i = 0; i < 2; i++) 
            entity = [self entityWithID:[NSNumber numberWithInt:arc4random() % 20 + 1]
                 inManagedObjectContext:backgroundContext];
            [entity setValue:[NSString stringWithFormat:@"A Name %d", ++counter] forKey:@"name"];
        

        [self dumpEntitiesInManagedObjectContext:backgroundContext];

        NSError *error;
        [backgroundContext save:&error];
        if (error) NSLog(@"%@ (%@)", [error localizedDescription], [error localizedFailureReason]);

        [backgroundContext.parentContext performBlock:^
            [self dumpEntitiesInManagedObjectContext:backgroundContext.parentContext];
        ];
    ];

该方法导入两个实体。 -entityWithID: 获取具有指定 ID 属性的实体,如果它不存在,则使用 NSEntityDescription -insertNewObjectForEntityForName: 创建一个。 -dumpEntitiesInManagedObjectContext: 转储所有实体以记录(一次在导入上下文中,一次在文档上下文中)。

问题是当文档被自动保存并完成一些额外的导入时,我在日志中得到以下内容:

[1140b] Entities: 10             [fb03] Entities: 11
[1140b]   2: A Name 1            [fb03]   2: A Name 1
[1140b]   3: A Name 4            [fb03]   3: A Name 4
[1140b]   4: A Name 8            [fb03]   4: A Name 8
[1140b]   5: A Name 12           [fb03]   5: A Name 12
[1140b]   6: A Name 10           [fb03]   6: A Name 10
[1140b]   8: A Name 6            [fb03]   8: A Name 6
[1140b] **12: A Name 11**        [fb03] **12: A Name 11**
[1140b]   13: A Name 9           [fb03] **12: A Name 5**
[1140b]   17: A Name 3           [fb03]   13: A Name 9
[1140b]   18: A Name 2           [fb03]   17: A Name 3
                                 [fb03]   18: A Name 2

导入上下文有 10 个实体,但主上下文有 11 个实体,并且 ID 为 12 的实体是重复的。似乎旧对象没有在父上下文中修改,而是添加了。

【问题讨论】:

【参考方案1】:

我仍然沉浸在所有这些东西中(Core Data 和 UIManagedDocument 一起工作),但我认为这个问题可能会解决你的情况:Core Data managed object does not see related objects until restart Simulator

它涉及在“正常”流程之前强制临时 id 永久存在,使用: [context obtainPermanentIDsForObjects:[inserts allObjects] error:&amp;error]

【讨论】:

谢谢,在-save: 帮助之前在后台上下文中调用-obtainPermanentIDsForObjects:error:【参考方案2】:

我遇到了这个问题,在NSEntityDescription做了一个小分类来解决这个问题:

@implementation NSEntityDescription (PermanentID)
+ (id)insertNewPermanentObjectForEntityForName:(NSString *)entityName
                        inManagedObjectContext:(NSManagedObjectContext *)context

    id object = [self insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
    NSError *error;
    if (![context obtainPermanentIDsForObjects:[NSArray arrayWithObject:object] error:&error]) 
        NSLog(@"Permanent ID not given");
    
    if (error) 
        NSLog(@"%@", error);
    
    return object;

还有更多关于 it here on my site 的内容,但我在此处复制了该类别,因此您不必点击。

【讨论】:

以上是关于在 iOS 5 中使用 UIManagedDocument 和父/子上下文导入核心数据背景的主要内容,如果未能解决你的问题,请参考以下文章

我应该在 iOS 5 中使用哪个 FB SDK 版本?

无法使用 iOS 4.3.5 设备在 Xcode 4.1 中构建源代码

在 iOS 5 中使用 AFNetworking 解析 JSON

在iPhone 4或iOS 5中可以直接使用Wifi吗?

在 Xcode 5 中支持 iOS 5 和 iOS 6

在使用Xcode 4.2和iOS 5进入main()之前,iOS应用程序崩溃