启动 Core Data 应用程序时在后台配置 NSFetchedResultsController
Posted
技术标签:
【中文标题】启动 Core Data 应用程序时在后台配置 NSFetchedResultsController【英文标题】:Configure NSFetchedResultsController in background when launching Core Data app 【发布时间】:2012-06-06 05:26:06 【问题描述】:我已经使用通常的样板代码设置了一个 Core Data 应用程序,并且 RootViewController 通过调用以下代码来初始化 FRC:
- (NSFetchedResultsController *)fetchedResultsController
if (__fetchedResultsController != nil)
return __fetchedResultsController;
// configure the fetchRequest, sectionKey and cacheName
__fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest: fetchRequest
managedObjectContext: self.managedObjectContext
sectionNameKeyPath: sectionKey
cacheName: cacheName];
return __fetchedResultsController;
我见过的所有示例代码都是这样做的。但是,我有一个庞大的数据集和超过 15,000 个条目,在 iPhone 4S 上启动应用程序大约需要 5 秒。这是启用缓存(没有它,需要 11 秒)和索引属性。
所以我希望能够显示应用程序正在等待加载的UIActivityIndicatorView
。我知道通常如何在后台线程中加载核心数据对象,然后将它们合并回主线程,但是如何在后台线程中初始化 FRC,以便在后台加载和分割所有对象?
我知道我可以加载所有对象并将它们在后台线程中分区到自定义字典中并使用它来呈现数据,但我更愿意使用标准的 FRC 调用和委托。
谢谢。
【问题讨论】:
【参考方案1】:我不太确定在后台使用 NSFetchedResultsController
是什么意思,但根据我的经验,您可以简单地为获取请求设置批量大小,如下所示:
[fetchRequest setFetchBatchSize:20];
以这种方式,在启动期间加载前 20 个元素,当您滚动下一个 20 时,依此类推。此外,您可以使用- (void)setPropertiesToFetch:(NSArray *)values
选择要获取的属性。
另一种方法是让(后台)任务开始在后台获取对象。我认为当在后台获取对象时,对象会以某种方式被缓存(但我不太确定),因此您可以更快地从主线程访问它们。
希望对你有帮助。
【讨论】:
fetchBatchSize 有点帮助,但它不能解决我的问题(我的代码中已经有了它)。我需要一个带有索引的分段 UITableView。我依靠 FRC 来计算这些部分,所以无论我给它什么批量大小,它都需要从数据库中读取所有行,找到它们的部分并将对象放入正确的存储桶中。现在在主线程上执行此操作会使应用程序启动后线程停止 5 秒,这显然不是一个很好的体验。 对于大型数据集 (ios 5),如果您设置了 sectionNamekeyPath,则无论批量大小如何,都会获取所有数据。 @Neil 抱歉,您在哪里找到该参考资料? 这对我帮助很大。 1 投票赞成。 sectionNameKeyPath 的一种解决方案是按照 Ben Blakely 的建议对数据进行非规范化:cimgf.com/2013/01/03/…【参考方案2】:我想我已经想通了...您可以在后台线程中创建 FRC 并在主线程中进行获取:
- (NSFetchedResultsController *)fetchedResultsController
if (__fetchedResultsController != nil)
return __fetchedResultsController;
// create something to pass back to the first time FRC is initialized without fetching
__block NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
[self.list_spinner startAnimating];
dispatch_async(self.filterMainQueue, ^
NSFetchedResultsController *newFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest: fetchRequest
managedObjectContext: self.managedObjectContext
sectionNameKeyPath: sectionKey
cacheName: cacheName];
dispatch_async(dispatch_get_main_queue(), ^
// stop the spinner here
[self.list_spinner stopAnimating];
NSError *error = nil;
if (![newFetchedResultsController performFetch:&error])
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
[SimpleListAppDelegate showCoreDataError: @"SimpleListViewController - FRC"];
__fetchedResultsController = nil;
newFetchedResultsController.delegate = self;
__fetchedResultsController = newFetchedResultsController;
[self.tableView reloadData];
);
);
return aFetchedResultsController;
【讨论】:
我是线程概念的新手。但我听说NSOperationQueue
是NSThread
的最佳替代品。我目前正在工作的项目使用操作队列。通过创建异步操作,这确实可以正常工作。
NSOperationQueue 比使用 NSThreads 更好,但使用 GCD(dispatch_async 的来源)甚至更好。我建议您熟悉 GCD 框架,因为 Apple 将来会越来越依赖它。
非常感谢朋友提供这些重要信息。但我的问题又是 为什么 Apple 会越来越依赖 GCD 框架? 有什么具体原因吗?
因为 a) 效率更高,因为系统不会每次都创建线程,它共享一个它自己管理的线程池,b) 开发人员使用它比直接使用 NSThreads 更容易(一旦你了解它)。
您在哪个线程中创建了 self.managedObjectContext?你在不同的线程中使用它,这是一个坏主意。以上是关于启动 Core Data 应用程序时在后台配置 NSFetchedResultsController的主要内容,如果未能解决你的问题,请参考以下文章