CoreData和线程

时间:2012-08-25 09:47:31

标签: ios core-data

我使用以下代码从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服务的访问在后台运行。 之后,在主线程中调用存储。应该没问题吧?

不幸的是,整个工作并不适用于所有设备。在我的所有设备和大多数其他设备上,没有问题。 在一些但显然是这样,因为不幸的是在商店里有一些负面的Bewertugnen和几个问题。 问题是他发现数据库中已有的记录而不是新的投资

感谢您的帮助, 斯蒂芬

2 个答案:

答案 0 :(得分:1)

我很难理解你的实际问题。我不太确定你的意思......

  

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

所以我会尽我所能。

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

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

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

[managedDocument updateChangeCount:UIDocumentChangeDone];

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

这个SO问题在某种程度上解决了这个问题。

Core Data could not fullfil fault for object after obtainPermanantIDs

基本上,考虑到您希望继续使用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);

答案 1 :(得分:0)

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

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

我自己刚刚转换为使用iOS5中的私有队列功能,在这种情况下,每次访问都是通过'[moc perform block ...]'完成的 - 转换花了我不到1小时,代码实际得到了clean(我之前一直在使用串行队列)。