我有一个带有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等任务,并删除其对象?
答案 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上保存,但这开始变得混乱。更容易确保两个竞争块不能重叠。