根据CoreData专家Marcus S. Zarra提出的三层CoreData堆栈,我参考了Olivier Drobnik的优秀文章并实现了它:
与此图示不同的是,我只使用一个临时后台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
。这是我的测试视图控制器的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个对象,当数据填充到表视图中时,UI会冻结。 Florian Kugler 使用这种架构进行了一项测试,插入了15000个对象,并通过仪器获得了主线程使用情况(蓝色表示主线程,灰色表示其他线程): 但是,这是我的主线程CPU使用情况,插入5000个对象时,使用iPhone 5进行分析: 如您所见,我的主线程使用情况远高于Florian的,并且我的UI也会冻结几秒钟。我的问题是,我做错了什么吗?当使用具有
NSFetchedResultsController
和UITableView
的三层MOC架构时,这是预期的行为吗?我知道插入5000个对象不是大多数应用程序的常规行为,因此当我尝试使用50或100个对象时,冻结不存在或不明显,但是主线程使用率很高(尽管我承认在这种情况下可能是由于其他原因,例如唤醒应用程序)。