在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; }
这段代码非常简单.我只使用mainUIManagedObjectContext
中的复制上面的图表NSMainQueueConcurrencyType
.每次importManagedObjectContext
保存子上下文时,都会触发通知,并且所有父上下文都会在其当前线程中执行保存.
我已经实现了一个带有a UITableView
和NSFetchedResultsController
附加的测试视图控制器.这是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
运行循环中插入了几个Task实体:
- (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个对象,当数据填充到表视图中时,UI正在冻结.Florian Kugler 用这个架构进行了测试,插入了15.000个对象和乐器,他得到了这个主线程用法(蓝色用于主线程,灰色用于任何其他线程):
但这是我的主要线程CPU使用5000个对象,使用iPhone 5进行分析:
正如您所看到的,我的主要线程使用量远远大于Florian的,并且我的UI冻结了几秒钟.我的问题是,我做错了什么?这是使用这个三层MOC架构a NSFetchedResultsController
和a 时的预期行为UITableView
吗?我知道插入5000个对象并不是大多数应用程序的常用行为,所以当我尝试使用50或100个对象时,冻结是不存在或不明显的,但主线程使用率很高(尽管我承认在这种情况下它可以是因为其他原因,如唤醒应用程序).
是的,这是预期的,因为主要的MOC参与其子女的拯救.当UI上下文的子项不进行大的保存时,它很方便,也很好,但如果这些保存更大,则常常会成为性能问题.使用此模式时,无法确定UI线程仅执行最小作业.
对于大型保存,我建议创建一个直接使用持久性存储协调器配置的上下文.发生大保存后,您只需在UI上下文中重新获取并可选地刷新数据.欲了解更多详情,请参阅我的答案在这里.