专用NSManagedObjectContexts和删除对象

时间:2013-08-01 06:11:18

标签: ios core-data

我有一个带有NSMainQueueConcurrencyType的主要托管对象上下文的Core Data堆栈。

用户可以在托管对象上启动可能需要很长时间的任务,因此可以在单独的上下文中执行:

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:mainMOC];
Person *samePerson = (Person *)[context objectWithID:person.objectID];
[context performBlock:^{
    // BLOCK 1
    // do lots of work

    // then update the managed object
    samePerson.value = someCalculatedValue;

    // save the private context
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Error: %@", error);
    }

    [mainMOC performBlock:^{
        NSError *error;
        if (![mainMOC save:&error]) {
            NSLog(@"Error saving: %@", error);
        }
    }];
}];

这很好用,主MOC正确更新,NSFetchedResultsController连接到它正常运行等等。

问题在于删除。我有这个设置来删除对象:

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:mainMOC];
[context performBlock:^{
    // BLOCK 2
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Person"];
    NSError *error;
    NSArray *all = [context executeFetchRequest:request error:&error];
    if (!all) {
        NSLog(@"Error fetching: %@", error);
    } else {
        for (NSManagedObject *person in all) {
            [context deleteObject:person];
        }
        NSError *error;
        if (![context save:&error]) {
            NSLog(@"Error saving: %@", error);
        }

        [mainMOC performBlock:^{
            NSError *error;
            if (![mainMOC save:&error]) {
                NSLog(@"Error saving: %@", error);
            }
        }];
    }
}];

现在,如果我在执行长时间任务(块1)所需的时间内执行删除操作(块2),则删除操作完成快速,并保存到主要上下文。在Block 1完成一段时间后,当它将mainMOC保存到最后时,我们得到了一个看似明显的崩溃:

CoreData could not fulfill a fault for ...

我的问题是:如何执行Block 1等任务,并删除其对象?

1 个答案:

答案 0 :(得分:1)

如果这些都是无法同时运行的后台任务,请尝试使用信号量来保护对它们的访问。

例如。对于实例变量:

dispatch_semaphore_t _backgroundProcessingSemaphore;

Lazily使用类似的东西初始化:

- (dispatch_semaphore_t)backgroundProcessingSemaphore
{
    if (!_backgroundProcessingSemaphore) {
        _backgroundProcessingSemaphore = dispatch_semaphore_create(1);
    }
    return _backgroundProcessingSemaphore;
}

将关键代码包围在:

dispatch_semaphore_wait(self.backgroundProcessingSemaphore, DISPATCH_TIME_FOREVER);
// Critical code
dispatch_semaphore_signal(self.backgroundProcessingSemaphore);

然后,只有一个关键的代码段可以在任何时间点运行。如果信号量已被占用,则调用dispatch_semaphore_wait的块将阻塞,直到它被释放。

你也可能想要考虑将你的长期任务分开,以便它可以在不连续的批次中运行,如果你还没有这样做 - 如果长时间运行的后台任务计时器即将到期,这很有用仍有工作要做 - 您可以在下次启动时从适当的点停止并重新启动。

其他选项将涉及在块2保存之前强制在块1上保存,但这开始变得混乱。更容易确保两个竞争块不能重叠。