Question:如何让我的子上下文看到父上下文上持久的更改,从而触发我的NSFetchedResultsController更新UI?
Here's the setup:个
你有一个应用程序可以下载并添加大量XML数据(大约200万条记录,每条记录的大小大约相当于一段正常的文本).sqlite文件的大小约为500 MB.将这些内容添加到核心数据中需要时间,但您希望用户能够在数据以增量方式加载到数据存储时使用应用程序.它必须是不可见的,用户无法察觉,大量数据正在移动,所以没有挂起,没有抖动:像黄油一样滚动.不过,应用程序更有用,添加的数据越多,所以我们不能永远等待数据添加到核心数据存储中.在代码中,这意味着我真的希望在导入代码中避免这样的代码:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
这款应用程序只支持iOS5,所以它需要支持的最慢的设备是iPhone3GS.
以下是我迄今为止开发当前解决方案所使用的资源:
Apple's Core Data Programming Guide: Efficiently Importing Data
- 使用自动释放池来降低内存
- 关系成本.导入平面,然后在末尾修补关系
- 别问你能不能帮上忙,它会以O(n^2)的方式放慢速度
- 批量导入:保存、重置、排空和重复
- 在导入时关闭undo撤消管理器
iDeveloper TV - Core Data Performance个
- 使用3种上下文:主上下文、主上下文和限制上下文类型
iDeveloper TV - Core Data for Mac, iPhone & iPad Update个
- 使用PerformBlock在其他队列上运行保存可以加快速度.
- 加密会减慢速度,如果可以,请将其关闭.
Importing and Displaying Large Data Sets in Core Data by Marcus Zarra个
- 您可以通过给当前运行循环留出时间来减慢导入速度. 因此,对于用户来说,事情感觉很顺利.
- 示例代码证明,可以执行大型导入并保持UI响应,但不如使用3个上下文和异步保存到磁盘的速度快.
我当前的解决方案
我有3个NSManagedObjectContext实例:
masterManagedObjectContext-这是具有NSPersistentStoreCoordinator并负责保存到磁盘的上下文.我这样做是为了我的存储可以是异步的,因此速度非常快.我在发布时创建它,如下所示:
masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
mainManagedObjectContext-这是UI在任何地方使用的上下文.它是master ManagedObjectContext的子级.我这样创建它:
mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[mainManagedObjectContext setUndoManager:nil];
[mainManagedObjectContext setParentContext:masterManagedObjectContext];
backgroundContext-这个上下文是在负责将XML数据导入核心数据的NSOperation子类中创建的.我在操作的主方法中创建它,并将其链接到主上下文.
backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[backgroundContext setUndoManager:nil];
[backgroundContext setParentContext:masterManagedObjectContext];
这实际上起作用的速度非常非常快.仅通过执行这3个上下文设置,我就能够将导入速度提高10倍以上!老实说,这很难相信.(此基本设计应该是标准核心数据模板的一部分.)
在导入过程中,我以两种不同的方式保存.我在后台上下文中保存的每1000个项目:
BOOL saveSuccess = [backgroundContext save:&error];
然后,在导入过程结束时,我保存主/父上下文,它表面上将修改推送到包括主上下文在内的其他子上下文:
[masterManagedObjectContext performBlock:^{
NSError *parentContextError = nil;
BOOL parentContextSaveSuccess = [masterManagedObjectContext save:&parentContextError];
}];
Problem:问题是在重新加载视图之前,我的UI不会更新.
我有一个简单的UIViewController,它有一个使用NSFetchedResultsController馈送数据的UITableView.当导入过程完成时,NSFetchedResultsController See的父/主上下文没有变化,因此UI不会像我习惯看到的那样自动更新.如果我从堆栈中弹出UIViewController并再次加载它,所有数据都在那里.
Question:如何让我的子上下文看到父上下文上持久的更改,从而触发我的NSFetchedResultsController更新UI?
我try 了以下应用程序挂起的方法:
- (void)saveMasterContext {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
NSError *error = nil;
BOOL saveSuccess = [masterManagedObjectContext save:&error];
[notificationCenter removeObserver:self name:NSManagedObjectContextDidSaveNotification object:masterManagedObjectContext];
}
- (void)contextChanged:(NSNotification*)notification
{
if ([notification object] == mainManagedObjectContext) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:YES];
return;
}
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}