У меня есть следующее дерево объектов:
Name Project
Users nil
John nil
Documents nil
Acme Project Acme Project <--- User selects a project
Proposal.doc Acme Project
12:32-12:33 Acme Project
13:11-13:33 Acme Project
...thousands more entries here...
Пользователь может назначить группу проекту. Все потомки настраиваются на этот проект.
Это блокирует основной поток, поэтому я использую NSOperations.
Я использую одобренный Apple способ сделать это, наблюдая за NSManagedObjectContextDidSaveNotification
и объединяю в основной контекст.
Мои сохранения завершались ошибкой из-за следующей ошибки:
Не удалось обработать ожидающие изменения перед сохранением. После 100 попыток контекст все еще остается грязным. Обычно это рекурсивное загрязнение вызвано неправильным методом проверки, -willSave или обработчиком уведомлений.
Я избавился от всех сложностей своего приложения и создал самый простой проект, который я мог придумать оф. И ошибка все равно возникает. Я пробовал:
Установка максимального количества операций в очереди на 1 или 10.
Вызов refreshObject: mergeChanges:
в нескольких точках подкласса NSOperation.
Установка политик слияния в контексте управляемого объекта.
Построение и анализ. Он оказывается пустым.
Как мне установить отношения в NSOperation без сбоев моего приложения? Разве это не может быть ограничением Core Data? Может?
Скачать мой проект: http://synapticmishap.co.uk/CDMTTest1. zip
Главный контроллер
@implementation JGMainController
-(IBAction)startTest:(id)sender {
NSManagedObjectContext *imoc = [[NSApp delegate] managedObjectContext];
JGProject *newProject = [JGProject insertInManagedObjectContext:imoc];
[newProject setProjectName:@"Project"];
[imoc save];
// Make an Operation Queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1]; // Also crashes with a higher number here (unsurprisingly)
NSSet *allTrainingGroupsSet = [imoc fetchAllObjectsForEntityName:@"TrainingGroup"];
for(JGTrainingGroup *thisTrainingGroup in allTrainingGroupsSet) {
JGMakeRelationship *makeRelationshipOperation = [[JGMakeRelationship alloc] trainGroup:[thisTrainingGroup objectID] withProject:[newProject objectID]];
[queue addOperation:makeRelationshipOperation];
makeRelationshipOperation = nil;
}
}
// Called on app launch.
-(void)setupLotsOfTestData {
// Sets up 10000 groups and one project
}
@end
Операция установления связи
@implementation JGMakeRelationshipOperation
-(id)trainGroup:(NSManagedObjectID *)groupObjectID_ withProject:(NSManagedObjectID *)projectObjectID_ {
appDelegate = [NSApp delegate];
imoc = [[NSManagedObjectContext alloc] init];
[imoc setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
[imoc setUndoManager:nil];
[imoc setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:imoc];
groupObjectID = groupObjectID_;
projectObjectID = projectObjectID_;
return self;
}
-(void)main {
JGProject *project = (JGProject *)[imoc objectWithID:projectObjectID];
JGTrainingGroup *trainingGroup = (JGTrainingGroup *)[imoc objectWithID:groupObjectID];
[project addGroupsAssignedObject:trainingGroup];
[imoc save];
trainingGroupObjectIDs = nil;
projectObjectID = nil;
project = nil;
trainingGroup = nil;
}
-(void)mergeChanges:(NSNotification *)notification {
NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
-(void)finalize {
appDelegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
imoc = nil;
[super finalize];
}
@end
@implementation NSManagedObjectContext (JGUtilities)
-(BOOL)save {
// If there's an save error, I throw an exception
}
@end
Модель данных
Обновление 1
Я экспериментировал еще несколько раз, и даже без слияния исключение все равно генерируется. Достаточно просто сохранить контекст управляемого объекта в другом потоке после изменения отношения.
У меня есть общий координатор постоянного хранилища с делегатом приложения. Я пробовал создать отдельный NSPersistentStoreCoordinator для потока с тем же URL, что и мое хранилище данных, но Core Data жалуется.
Мне бы хотелось услышать предложения о том, как я могу сделать координатора для потока. В основных документах данных упоминается, что есть способ сделать это, но я не понимаю, как это сделать.