4

this excellent post由Olivier Drobnik,我實現了由CoreData大師馬庫斯S. Zarra提出的三層CoreData堆棧:CoreData孩子上下文,NSFetchedResultsController和主線程

Three-layer CoreData context architecture

從這個唯一的區別圖表和我的代碼是,我只使用一個臨時背景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]; 
} 

在這裏,我附上mainUIManagedObjectContextNSFetchedResultsController。後來,在我的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對象,並與儀器,他得到這個主線程使用(藍色是主線程,灰色的任何其他線程):

Main thread CPU usage with three-layer context architecture

但這裏是我的主線程的CPU使用率與5000點的對象,異形使用iPhone 5:

enter image description here

正如你看到的,我的主線程的使用遠遠大於弗洛裏安之更大,也是我的UI凍結幾秒鐘。我的問題是,我做錯了什麼?這是在使用這種三層MOC體系結構時使用NSFetchedResultsControllerUITableView時的預期行爲嗎?我知道插入5000個對象不是大多數應用程序的常見行爲,所以當我嘗試了50個或100個對象時,凍結不存在或不明顯,但主線程使用率很高(儘管我承認在這種情況下它可以由於喚醒應用程序等其他原因)。

+0

最好每隔幾百次插入執行一次saveContext,而不是每次插入一次。在你使用它的時候,把每個批處理放入一個@autoreleasepool來限制內存的使用。 –

回答

5

是的,可以預料,因爲主要交通部門參與其子女的救助。當UI上下文的孩子沒有做大的保存時,這是很方便和好的,但如果這些保存更大的話,往往會成爲性能問題。在使用這種模式時,你不能確定UI線程只做最低限度的工作。

對於大型保存,我建議創建一個直接使用持久存儲協調器配置的上下文。在大量保存發生後,您只需重新提取並在UI上下文中刷新數據。欲瞭解更多詳情,請參閱我的回答here

相關問題