Основные данные не могли полностью выполнить ошибку для объекта после получения данных PermanantID

Я получаю данные с веб-сервера, обрабатывая его в дочернем закрытом фоновом контексте с именем backgroundMOC. Это дочерний элемент mainMOC, который связан с основным пользовательским интерфейсом, поэтому сохранение в backgroundMOC изменений пользовательского интерфейса триггеров. mainMOC - это дочерний элемент masterMOC, который является частной фоновой очереди, привязанной к постоянному хранилищу, поэтому сохранение на главном диске сохраняется на диск.

Теперь я получаю данные, создаю новые объекты на backgroundMOC, а затем сохраняю backgroundMOC (так что обновляется пользовательский интерфейс), сохраняйте mainMOC, (так что я могу почти сохранить на диск) и сохранить masterMOC (чтобы я мог, наконец, записать на диск). Проблема заключается в том, что когда объект появляется в пользовательском интерфейсе с помощью выбранного контроллера результатов, objectId по-прежнему является временным.

Это вызывает проблемы с повторяющимися проблемами строк, где, если я получаю те же данные с сервера (случайно), мой backgroundMOC не знает, что этот объект уже существует, поскольку ему не был назначен постоянный идентификатор, поэтому он создает другой объект. Когда я перезапускаю приложение, дублирующий объект исчезает, поэтому я знаю, что это просто проблема с сопоставлением идентификаторов.

Итак, я подумал, что могу попробовать

[backgroundMOC obtainPermanentIDsForObjects:backgroundMOC.registeredObjects.allObjects error:nil];

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

CoreData could not fulfill a fault for...

Если у вас есть какие-то намеки, которые могут привести меня в правильном направлении, пожалуйста, поделитесь. Благодаря

Изменить: Хорошо, поэтому изначально я вызывал getPermanentIDsForObjects на backgroundMOC, который является дочерним элементом mainMOC, который является дочерним элементом masterMOC. Я переключил его так, чтобы получить идентификаторы на mainMOC, и он решил все мои проблемы (пока). Я никогда не должен был называть getPermIds для детского контекста?

Ответы

Ответ 1

Это известная ошибка (вложенные контексты, не получающие постоянные идентификаторы при сохранении новых объектов), могут и должны быть исправлены в предстоящей версии...

Однако вы должны иметь возможность запрашивать постоянные идентификаторы, но вы должны запрашивать их только на объекты, которые были вставлены.

[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:0];

Вы должны сделать это, прежде чем сохранять MOC, хотя, поскольку, если вы сохраняете без получения постоянных идентификаторов, временные идентификаторы распространяются в родительские контексты. Например, в вашем случае, когда вы сохраняете в mainMoc, затем получаете IDS, у backgroundMOC все еще есть временные идентификаторы, поэтому в будущем из него будут создаваться дубликаты данных.

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

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

[backgroundMoc performBlock:^{
    [backgroundMoc obtainPermanentIDsForObjects:backgroundMoc.insertedObjects.allObjects error:0];
    [backgroundMoc save:0];
    [mainMoc performBlock:^{
       [mainMoc save:0];
        [masterMoc performBlock:^{
            [masterMoc save:0];
        }];
    }];
}];

Есть и другие игры, которые вы можете играть, если хотите.

Предоставить категорию в NSManagedObject, подобную этой...

@implementation NSManagedObject (initWithPermanentID)
- (id)initWithEntity:(NSEntityDescription *)entity insertWithPermanentIDIntoManagedObjectContext:(NSManagedObjectContext *)context {
    if (self = [self initWithEntity:entity insertIntoManagedObjectContext:context]) {
        NSError *error = nil;
        if (![context obtainPermanentIDsForObjects:@[self] error:&error]) {
            @throw [NSException exceptionWithName:@"CoreData Error" reason:error.localizedDescription userInfo:error.userInfo];
        }
    }
    return self;
}

+ (NSArray*)createMultipleObjects:(NSUInteger)count withEntity:(NSEntityDescription *)entity inManagedObjectContext:(NSManagedObjectContext *)context {
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
    for (NSUInteger i = 0; i < count; ++i) {
        [array addObject:[[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context]];
    }
    NSError *error = nil;
    if (![context obtainPermanentIDsForObjects:array error:&error]) {
        @throw [NSException exceptionWithName:@"CoreData Error" reason:error.localizedDescription userInfo:error.userInfo];
    }
    return array;
}
@end

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

Хорошо, это не самое лучшее, но оно принесло пользу. Кроме того, второй создает несколько объектов одного и того же объекта и одновременно захватывает их постоянные идентификаторы.

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

К сожалению, у вас не может быть отдельного MOC, просто сделайте запросы persistentID и передайте ObjectID, хотя у вас может быть отдельный MOC, создающий объекты-прототипы в БД и предоставляющие им ObjectID.

Прототип factory является довольно распространенным образцом, и если вы идете по этому маршруту, очень легко внести незначительные изменения, когда сюда попадут возможные исправления ошибок.

ИЗМЕНИТЬ

В ответ на Свен...

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

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

Если вы делаете крупномасштабное обновление, я предлагаю использовать "старый" метод. Создайте новый MOC, напрямую подключенный к PSC. Внесите все свои изменения там, и ваши "живые" контексты просто сливаются с уведомлениями DidSave.

Наконец, в базе данных влияют постоянные идентификаторы. Это нормально, чтобы отменить MOC. Диск удаляется, а метаданные изменяются, но объекты не сохраняются.

Честно говоря, я не делал большого теста, чтобы увидеть, есть ли в нем какое-то пустое пространство, поэтому вы можете сделать это и вернуться со мной.

Посмотрите на фактический размер файла базы данных на диске, затем создайте 10000 объектов, затем получите постоянные идентификаторы, отпустите MOC и посмотрите на размер снова.

Если есть влияние, вы можете попробовать удалить объекты или запустить вакуум в базе данных после больших обновлений, чтобы убедиться, что это работает.

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

Ответ 2

У меня возникло множество разочарований с Core Data при работе между потоками переднего и заднего фона. В поисках решения по одной из моих проблем я столкнулся с

Волшебная запись

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

Вы можете проверить это.