2013-08-01 2 views
1

У меня есть стек Core Data с основным контекстом управляемых объектов с NSMainQueueConcurrencyType.Private NSManagedObjectContexts и удаление объектов

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

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
[context setParentContext:mainMOC]; 
Person *samePerson = (Person *)[context objectWithID:person.objectID]; 
[context performBlock:^{ 
    // BLOCK 1 
    // do lots of work 

    // then update the managed object 
    samePerson.value = someCalculatedValue; 

    // save the private context 
    NSError *error; 
    if (![context save:&error]) { 
     NSLog(@"Error: %@", error); 
    } 

    [mainMOC performBlock:^{ 
     NSError *error; 
     if (![mainMOC save:&error]) { 
      NSLog(@"Error saving: %@", error); 
     } 
    }]; 
}]; 

Это прекрасно работает, и главный MOC обновляется должным образом, NSFetchedResultsController подключен к нему надлежащим образом и т. д.

Проблема с удалением. Я имею эту установку для удаления объектов:

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
[context setParentContext:mainMOC]; 
[context performBlock:^{ 
    // BLOCK 2 
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Person"]; 
    NSError *error; 
    NSArray *all = [context executeFetchRequest:request error:&error]; 
    if (!all) { 
     NSLog(@"Error fetching: %@", error); 
    } else { 
     for (NSManagedObject *person in all) { 
      [context deleteObject:person]; 
     } 
     NSError *error; 
     if (![context save:&error]) { 
      NSLog(@"Error saving: %@", error); 
     } 

     [mainMOC performBlock:^{ 
      NSError *error; 
      if (![mainMOC save:&error]) { 
       NSLog(@"Error saving: %@", error); 
      } 
     }]; 
    } 
}]; 

Теперь, если я эта операции удаления (блок 2) в течение времени, необходимым для выполнения длительной задачи (блок 1), то УДАЛИТЬ операция быстро заканчивается и сохраняется в основном контексте. Через некоторое время блок 1 заканчивается, и когда она сохраняет mainMOC в его конце, мы получаем, казалось бы, очевидный крах:

CoreData could not fulfill a fault for ... 

Мой вопрос: как я могу выполнить задачи, такие как блок 1 с возможностью его объект удаляется?

ответ

1

Если это обе фоновые задачи, которые не могут работать одновременно, попробуйте использовать семафоры для защиты доступа к ним.

например. для переменной экземпляра:

dispatch_semaphore_t _backgroundProcessingSemaphore; 

Лениво инициализируется используя что-то вроде:

- (dispatch_semaphore_t)backgroundProcessingSemaphore 
{ 
    if (!_backgroundProcessingSemaphore) { 
     _backgroundProcessingSemaphore = dispatch_semaphore_create(1); 
    } 
    return _backgroundProcessingSemaphore; 
} 

Surround критического кода с:

dispatch_semaphore_wait(self.backgroundProcessingSemaphore, DISPATCH_TIME_FOREVER); 
// Critical code 
dispatch_semaphore_signal(self.backgroundProcessingSemaphore); 

только один критический участок кода может затем работать в любой точке во время. Блок, который вызывает dispatch_semaphore_wait, блокирует, если семафор уже занят, пока он не будет освобожден.

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

Другие варианты включают в себя принудительное сохранение на блоке 1 до того, как блок 2 сохранит себя, но это начинает запутываться. Гораздо проще обеспечить, чтобы два конкурирующих блока не могли пересекаться.

+0

Я думаю, мне было интересно, был ли лучший образец для работы с удаленными управляемыми объектами в многопоточной среде. Я надеялся на что-то другое, кроме синхронизации или использования последовательной очереди. В противном случае весь мой управляемый объектный код потребует таких семафоров, не так ли? – raheel

+0

Посмотрите на установку mergePolicy в своем контексте и попробуйте ответить на сохранение как с помощью mergeChangesFromContextDidSaveNotification: чтобы попытаться объединить изменения из вашего удаления в работу, выполняемую в длинном контексте, перед сохранением. У вас есть два разных контекста, наступающих друг на друга, - будет какая-то работа, связанная с тем, чтобы заставить их сосуществовать - либо объединить изменения, либо разделить их. –

Смежные вопросы