正在关注 this excellent post通过 Olivier Drobnik,我实现了 CoreData 专家 Marcus S. Zarra 提出的三层 CoreData 堆栈:
与此图和我的代码的唯一区别是我只使用一个临时背景MOC,以避免在多个临时MOC中插入对象时出现重复。这是我的上下文初始化代码:
#pragma mark - NSManagedObjectContexts
+ (NSManagedObjectContext *)privateManagedObjectContext
{
if (!_privateManagedObjectContext) {
// Setup MOC attached to PSC
_privateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_privateManagedObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
// Add notification to perform save when the child is updated
_privateContextSaveObserver =
[[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
NSManagedObjectContext *savedContext = [note object];
if (savedContext.parentContext == _privateManagedObjectContext) {
[_privateManagedObjectContext performBlock:^{
NSLog(@"AMBCoreData -> saving privateMOC");
NSError *error;
if (![_privateManagedObjectContext save:&error]) {
NSLog(@"AMBCoreData -> error saving _privateMOC: %@ %@", [error localizedDescription], [error userInfo]);
}
}];
}
}];
}
return _privateManagedObjectContext;
}
+ (NSManagedObjectContext *)mainUIManagedObjectContext
{
if (!_mainUIManagedObjectContext) {
// Setup MOC attached to parent privateMOC in main queue
_mainUIManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainUIManagedObjectContext setParentContext:[self privateManagedObjectContext]];
// Add notification to perform save when the child is updated
_mainUIContextSaveObserver =
[[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
NSManagedObjectContext *savedContext = [note object];
if (savedContext.parentContext == _mainUIManagedObjectContext) {
NSLog(@"AMBCoreData -> saving mainUIMOC");
[_mainUIManagedObjectContext performBlock:^{
NSError *error;
if (![_mainUIManagedObjectContext save:&error]) {
NSLog(@"AMBCoreData -> error saving mainUIMOC: %@ %@", [error localizedDescription], [error userInfo]);
}
}];
}
}];
}
return _mainUIManagedObjectContext;
}
+ (NSManagedObjectContext *)importManagedObjectContext
{
if (!_importManagedObjectContext) {
// Setup MOC attached to parent mainUIMOC in private queue
_importManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_importManagedObjectContext setParentContext:[self mainUIManagedObjectContext]];
}
return _importManagedObjectContext;
}
这段代码非常简单。我仅使用 NSMainQueueConcurrencyType
中的 mainUIManagedObjectContext
复制上图。每次保存子上下文 importManagedObjectContext
时,都会触发一个通知,并且所有父上下文都会在其当前线程中执行保存操作。
我已经实现了一个带有 UITableView
和 NSFetchedResultsController
的测试 View Controller 。这是我的测试 View Controller 的 viewDidLoad
中的代码:
- (void)viewDidLoad
{
[super viewDidLoad];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Task"];
[request setSortDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"insertDate" ascending:NO]]];
self.fetchRequest = request;
NSFetchedResultsController *frc =
[[NSFetchedResultsController alloc]
initWithFetchRequest:self.fetchRequest
managedObjectContext:[AMBCoreData mainUIManagedObjectContext]
sectionNameKeyPath:nil
cacheName:nil];
frc.delegate = self;
[self setFetchedResultsController:frc];
[self.fetchedResultsController performFetch:nil];
}
在这里,我将 mainUIManagedObjectContext
附加到 NSFetchedResultsController
。稍后,在我的 viewDidAppear
中,我运行一个循环来插入一些任务实体:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[AMBCoreData importManagedObjectContext] performBlock:^{
for (int i = 0; i < 5000; i++) {
Task *task = [NSEntityDescription insertNewObjectForEntityForName:@"Task" inManagedObjectContext:[AMBCoreData importManagedObjectContext]];
task.title = [NSString stringWithFormat:@"Task %d", i];
task.insertDate = [NSDate new];
[[AMBCoreData importManagedObjectContext] save:nil];
}];
}
问题是,我插入了 5000 个对象,当数据填充到 TableView 时,UI 卡住了。弗洛里安·库格勒 ran a test with this architecture插入 15.000 个对象并使用 instruments 他得到了主线程的使用(蓝色代表主线程,灰色代表任何其他线程):
但这是我的 5000 个对象的主线程 CPU 使用情况,使用 iPhone 5 进行分析:
如您所见,我的主线程使用率远高于 Florian,而且我的 UI 卡住了几秒钟。我的问题是,我做错了什么吗?这是使用带有 NSFetchedResultsController
和 UITableView
的三层 MOC 架构时的预期行为吗?我知道插入 5000 个对象不是大多数应用程序的通常行为,所以当我尝试使用 50 或 100 个对象时,卡住不存在或不明显,但主线程使用率很高(尽管我承认在这种情况下它可以由于其他原因,如唤醒应用程序)。
最佳答案
是的,这是意料之中的,因为主要 MOC 参与了其 child 的储蓄。当 UI 上下文的子级不进行大保存时,这很方便并且还可以,但如果这些保存较大,通常会成为性能问题。使用此模式时,您无法确定 UI 线程只完成最少的工作。
为了节省大量资金,我建议创建一个直接使用持久存储协调器配置的上下文。大保存发生后,您只需重新获取并可选择刷新 UI 上下文中的数据。有关详细信息,请参阅我的回答 here .
关于ios - CoreData 子上下文、NSFetchedResultsController 和主线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21476559/