1

我有一个带有 NSMainQueueConcurrencyType 的主要托管对象上下文的核心数据堆栈。

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

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 之类的任务,并且其对象可能被删除?

4

1 回答 1

1

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

例如。对于实例变量:

dispatch_semaphore_t _backgroundProcessingSemaphore;

懒惰地使用类似的东西初始化:

- (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 进行保存,但这开始变得混乱。更容易确保两个竞争块不会重叠。

于 2013-08-01T08:55:44.103 回答