0

我使用以下代码从 Web 服务加载数据并将其存储在 Core Data DB 中。

    dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL);
dispatch_async(fetchQ, ^{
    NSArray *list = [WebService loadData];
    dispatch_sync(dispatch_get_main_queue(), ^{
        if (list != nil) {
            for (NSDictionary *data in list) {

                NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext];
                NSFetchRequest *request = [[NSFetchRequest alloc] init];
                [request setEntity:entityDescription];

                NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]];
                [request setPredicate:predicate];

                NSError *error = nil;
                NSArray *data = [appDelegate.coreDataDatabase.managedObjectContext executeFetchRequest:request error:&error];

                Object *object = [data objectAtIndex: 0];
                if (object == nil) {
                    object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext];
                    object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]];
                }

                object.name = [data objectForKey:@"name"];
            }

            [appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
        }
    });
});
dispatch_release(fetchQ);

代码被简化了,但它应该代表序列。对 Web 服务的访问在后台运行。之后,在主线程中调用 store。其实应该没问题吧?

不幸的是,并非所有设备都适用于所有设备。在我的所有设备和其他大多数设备上,都没有问题。在一些但显然如此,因为不幸的是商店里有一些负面的 Bewertugnen 和几个问题。问题是他在数据库中找到了一个已经存在的记录,而不是不断的新投资

谢谢你的帮助,斯特凡

4

2 回答 2

1

我很难理解你的实际问题。我不太清楚你的意思是什么......

在一些但显然如此,因为不幸的是商店里有一些负面的 Bewertugnen 和几个问题。问题是他在数据库中找到了一个已经存在的记录,而不是不断的新投资

所以我会采取我最好的猜测。

从代码中,我假设您使用的是 UIManagedDocument。

您不应该UIManagedDocument直接保存 (saveToUrl)。相反,您应该让它处理自动保存,因为它会适当地执行此操作。

如果您在NSUndoManager上激活,UIManagedDocument则无需执行任何操作。所有操作将自动保存。如果您没有撤消管理器,则需要戳它以使其知道它有脏数据。你这样做:

[managedDocument updateChangeCount:UIDocumentChangeDone];

但是,请注意UIManagedDocument使用嵌套上下文,并且有一些与插入新对象相关的错误。也就是说,持久性 ID 没有被正确地推回子上下文。我对您的问题的假设是您因此而获得重复的数据条目。

这个 SO question 在一定程度上解决了这个问题。

在获取 PermanantID 后,Core Data 无法完成对象的错误

基本上,你有几个选择,考虑到你想继续UIManagedDocument。首先,获取永久 ID。替换这一行:

[appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];

有了这个(当然——处理错误——这只是传递0):

NSManagedObjectContext *moc = appDelegate.coreDataDatabase.managedObjectContext;
[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:0];
[appDelegate.coreDataDatabase updateChangeCount:UIDocumentChangeDone];

这仍然存在一些问题,但它们非常罕见。您的代码似乎没有非常复杂的对象图,所以这就足够了。

另一种选择是导入单独的 MOC(UIManagedDocument父上下文的子级),并在导入完成后重新获取主上下文。

使用您的代码示例,一个简单的方法是......

dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL);
dispatch_async(fetchQ, ^{
    NSArray *list = [WebService loadData];
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
    moc.parentContext = appDelegate.coreDataDatabase.managedObjectContext.parentContext;
    for (NSDictionary *data in list) {
        NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:moc];
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        [request setEntity:entityDescription];

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]];
        [request setPredicate:predicate];

        NSError *error = nil;
        NSArray *data = [moc executeFetchRequest:request error:&error];
        Object *object = [data objectAtIndex: 0];
        if (object == nil) {
            object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:moc];
            object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]];
        }

        object.name = [data objectForKey:@"name"];
    }

    // Save the MOC you just loaded everything into
    [moc save:&error]; // handle error...
    // Save the parent MOC, which is also the parent of your main MOC
    moc = moc.parentContext;
    [moc performBlock:^{
        [moc save:&error]; // handle error
        // When all the saves are done, run on the main queue and just refetch
        // all the entities that were inserted.
        dispatch_async(dispatch_get_main_queue(), ^{
            // Here, use appDelegate.coreDataDatabase.managedObjectContext
            // to refetch the objects that were altered.  If you are using a
            // table view or FRC just issue a refetch of the data and the
            // main MOC will get everything that was modified.
        });
    }];
});
dispatch_release(fetchQ);
于 2012-08-25T15:44:11.557 回答
0

如果您在主线程上创建了上下文,那么对它的每次访问也必须在主线程上(假设 iOS 5 之前的配置 - Core Data 支持 iOS 5 之后的 3 种模式)。

您可以创建一个串行调度队列,在那里创建 moc,并通过该队列汇集对它的所有访问。调度队列将访问序列化,因此您永远不会有并发。

我自己刚刚转换为使用 iOS5 中的私有队列功能,在这种情况下,每次访问都由 '[moc perform block...]' 完成 - 我花了不到 1 小时的时间进行转换,而且代码实际上变得更清晰(我如前所述,一直在使用串行队列)。

于 2012-08-25T12:38:39.447 回答