繼this excellent post由Olivier Drobnik,我實現了由CoreData大師馬庫斯S. Zarra提出的三層CoreData堆棧:CoreData孩子上下文,NSFetchedResultsController和主線程
從這個唯一的區別圖表和我的代碼是,我只使用一個臨時背景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被凍結。弗洛裏安·庫格勒ran a test with this architecture插入15.000對象,並與儀器,他得到這個主線程使用(藍色是主線程,灰色的任何其他線程):
但這裏是我的主線程的CPU使用率與5000點的對象,異形使用iPhone 5:
正如你看到的,我的主線程的使用遠遠大於弗洛裏安之更大,也是我的UI凍結幾秒鐘。我的問題是,我做錯了什麼?這是在使用這種三層MOC體系結構時使用NSFetchedResultsController
和UITableView
時的預期行爲嗎?我知道插入5000個對象不是大多數應用程序的常見行爲,所以當我嘗試了50個或100個對象時,凍結不存在或不明顯,但主線程使用率很高(儘管我承認在這種情況下它可以由於喚醒應用程序等其他原因)。
最好每隔幾百次插入執行一次saveContext,而不是每次插入一次。在你使用它的時候,把每個批處理放入一個@autoreleasepool來限制內存的使用。 –