我最近重写了我的核心数据驱动的数据库控制器,以使用 Grand Central Dispatch 在后台管理获取和导入。控制器可以在 2 个 NSManagedContext 上操作:
NSManagedObjectContext *mainMoc
主线程的实例变量。此上下文仅用于主线程或dipatch_get_main_queue()
全局队列对 UI 的快速访问。NSManagedObjectContext *bgMoc
用于后台任务(为表的 NSFetchedresultsController 导入和获取数据)。此后台任务仅由用户定义的队列触发:(dispatch_queue_t bgQueue
数据库控制器对象中的实例变量)。
当执行更大或更复杂的谓词时,在后台获取表的数据不会阻塞用户 UI。
在我的表视图控制器中获取 NSFetchedResultsController 的示例代码:
-(void)fetchData{
dispatch_async([CDdb db].bgQueue, ^{
NSError *error = nil;
[[self.fetchedResultsController fetchRequest] setPredicate:self.predicate];
if (self.fetchedResultsController && ![self.fetchedResultsController performFetch:&error]) {
NSSLog(@"Unresolved error in fetchData %@", error);
}
if (!initial_fetch_attampted)initial_fetch_attampted = YES;
fetching = NO;
dispatch_async(dispatch_get_main_queue(), ^{
[self.table reloadData];
[self.table scrollRectToVisible:CGRectMake(0, 0, 100, 20) animated:YES];
});
});
} // fetchData 函数结束
bgMoc
mainMoc
与保存合并使用NSManagedObjectContextDidSaveNotification
:
- (void)bgMocDidSave:(NSNotification *)saveNotification {
// CDdb - bgMoc didsave - merging changes with main mainMoc
dispatch_async(dispatch_get_main_queue(), ^{
[self.mainMoc mergeChangesFromContextDidSaveNotification:saveNotification];
// Extra notification for some other, potentially interested clients
[[NSNotificationCenter defaultCenter] postNotificationName:DATABASE_SAVED_WITH_CHANGES object:saveNotification];
});
}
- (void)mainMocDidSave:(NSNotification *)saveNotification {
// CDdb - main mainMoc didSave - merging changes with bgMoc
dispatch_async(self.bgQueue, ^{
[self.bgMoc mergeChangesFromContextDidSaveNotification:saveNotification];
});
}
NSfetchedResultsController 委托只实现了一种方法(为简单起见):
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
dispatch_async(dispatch_get_main_queue(), ^{
[self fetchData];
});
}
这样,我试图遵循 Apple 对 Core Data 的建议:每个线程 1 个 NSManagedObjectContext。我知道这种模式并不完全干净,最后有两个原因:
bgQueue
挂起后不一定会触发同一个线程,但由于它是串行的,所以应该没什么关系(从来没有 2 个线程尝试访问bgMoc
专用于它的 NSManagedObjectContext)。- 有时表视图数据源方法会向 NSFetchedResultsController 询问来自 bgMoc 的信息(因为 fetch 是在 bgQueue 上完成的),例如节数、在节数中获取的对象等。
具有此缺陷的事件这种方法在 95% 的应用程序运行时间中运行良好,直到...
这是我的问题:
有时,应用程序会随机冻结但不会崩溃。它不会对任何触摸做出响应,使其恢复运行的唯一方法是完全重新启动它(切换回后台无济于事)。没有抛出异常,也没有任何内容打印到控制台(我在 Xcode 中为所有异常设置了断点)。
我尝试使用 Instruments(尤其是时间配置文件)对其进行调试,以查看主线程上是否发生了一些困难,但没有出现任何问题。
我知道 GCD 和 Core Data 是这里的主要嫌疑人,但我不知道如何跟踪/调试它。
让我指出,当我仅将所有任务异步分派到队列时也会发生这种情况(到处使用dispatch_async )。这让我觉得这不仅仅是标准的僵局。
是否有任何可能性或提示我如何获得更多信息?一些额外的调试标志、仪器魔术技巧或构建设置等......
非常感谢任何关于可能原因的建议以及(或)如何以更好的方式实现 NSFetchedResultsController 的后台获取和后台导入的指针。