Несколько NSManagedObjectContexts или один контекст и -performBlock

Я использую Core Data с одним NSManagedObjectContext в течение длительного времени, все операции по извлечению, сохранению и обновлению фона будут выполняться в одном контексте через вспомогательные классы, я планировал реализовать множественный подход NSManagedObjectContext который является рекомендуемым решением в большинстве моих поисков).

Мой вопрос: есть performBlock только для выполнения кода для этого контекста? Не можем ли мы сделать что-то вроде ниже:

- (void) checkSyncServer {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        //do something here, check and fetch data
        // create NSManagedObject 
        [_tempContext save:&error];  
        //masterContext will merge changes through notification observers
    });
}

(i.e) выполнить код, кроме метода -performBlock. Как выполнить несколько асинхронных методов и выполнить сохранение?

Тем не менее, я считаю, что один контекст (который управляется одним классом singleton NSObject) проще использовать.

Этот множественный контекст с ContextConcurrencyType выглядит более сложным (с точки зрения потока выполнения). Есть ли лучшее решение?

Ответ 1

Вы можете получить доступ к контекстам одним из двух способов:

  • В потоке/очереди. Это относится к ограниченным контекстам и контекстам основной очереди. Вы можете получить доступ к ним свободно из собственного потока.
  • С -performBlock:, если это контекст частной очереди или если вы касаетесь контекста из потока, отличного от того, к которому он принадлежит.

Вы не можете использовать dispatch_async для доступа к контексту. Если вы хотите, чтобы действие было асинхронным, вам нужно использовать -performBlock:.

Если вы использовали один контекст раньше, и вы касались его с помощью dispatch_async, вы нарушали правило ограничения потока.

Update

При вызове [[NSManagedObjectContext alloc] init], функционально эквивалентном [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType].

NSManagedObjectContext имеет всегда поток.

Что касается выполнения нескольких методов, вы можете просто вызвать их все в одном блоке:

NSManagedObjectContext *moc = ...;
[moc performBlock:^{
  //Fetch something
  //Process data
  //Save
}];

Или вы можете вложить их, если хотите, чтобы они были все асинхронны друг от друга:

NSManagedObjectContext *moc = ...;
[moc performBlock:^{
  //Fetch Something
  [moc performBlock:^{
    //Process Data
  }];
  [moc performBlock:^{
    //Save
  }];
}];

Так как -performBlock: безопасен для повторного входа, вы можете вложить их все, что хотите.

Обновить Async save

Чтобы выполнить сохранение async, у вас должно быть два контекста (или более):

  • Контекст главной очереди, с которым пользовательский интерфейс взаимодействует с
  • Контекст частной очереди, который сохраняет

Частный контекст имеет NSPersistentStoreCoordinator, а основной контекст очереди имеет закрытый как родительский.

Вся работа выполняется в контексте основной очереди, и вы можете безопасно ее сохранять, обычно в основном потоке. Это спасение будет мгновенным. После этого вы выполните сохранение async:

NSManagedObjectContext *privateMOC = ...;
NSManagedObjectContext *mainMOC = ...;

//Do something on the mainMOC

NSError *error = nil;
if (![mainMOC save:&error]) {
  NSLog(@"Main MOC save failed: %@\n%@", [error localizedDescription], [error userInfo]);
  abort();
}

[privateMOC performBlock:^{
  NSError *error = nil;
  if (![privateMOC save:&error]) {
    NSLog(@"Private moc failed to write to disk: %@\n%@", [error localizedDescription], [error userInfo]);
    abort();
  }
}];

Если у вас уже есть приложение, все, что вам нужно сделать, это:

  • Создайте свой собственный moc
  • Установите его как родительский элемент основного
  • Измените основной init
  • Добавить метод сохранения частного блока при каждом вызове save на основном

Вы можете реорганизовать оттуда, но это все, что вам действительно нужно изменить.