CoreData, NSFetchedResultsController child contexts and main thread

After this great post by Olivier Drobnik, I implemented the three-layer CoreData stack proposed by CoreData guru Marcus S. Zarra:

Three-layer CoreData context architecture

The only difference from this diagram and my code is that I use one temporary MOC focus to avoid duplication when inserting objects into multiple temporary MOCs. Here is my context initialization code:

#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;
}

This code is pretty simple. I copy the above diagram using only mainUIManagedObjectContextin NSMainQueueConcurrencyType. Each time a child context is importManagedObjectContextsaved, a notification is triggered, and all parent contexts store the current thread in it.

UITableView a 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 , , . Florian Kugler , 15 000 , ( , ):

Main thread CPU usage with three-layer context architecture

5000 , iPhone 5:

enter image description here

, , Florian, . : - ? MOC NSFetchedResultsController a UITableView? , 5000 - , , 50 100 , , ( , - , ).

+4
1

, , Main MOC . , , , . , .

, . , , , , . . .

+5

Source: https://habr.com/ru/post/1524668/


All Articles