Подтвердить что ты не робот

Основные данные. Получают ли дочерние контексты постоянные объектные идентификаторы для вновь вставленных объектов?

У меня есть приложение с двумя настройками контекста управляемого объекта:

  • Контекст родителя: NSPrivateQueueConcurrencyType, связанный с постоянным хранилищем.
  • Основной контекст: NSMainQueueConcurrencyType, дочерний элемент родительского контекста.

При вставке нового управляемого объекта в основной контекст я сохраняю основной контекст, а затем родительский контекст, например:

[context performBlockAndWait:^{
    NSError * error = nil;
    if (![context save: &error]) {
        NSLog(@"Core Data save error %@, %@", error, [error userInfo]);
    }
}];

[parentContext performBlock:^{
    NSError *error = nil;
    BOOL result = [parentContext save: &error];
    if ( ! result ) {
        NSLog( @"Core Data save error in parent context %@, %@", error, [error userInfo] );
    }
}];

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

Итак:

  • Означает ли постоянный идентификатор объекта автоматическое возвращение к основному (дочернему) контексту?
  • Когда я принудительно получаю постоянный идентификатор объекта с помощью [NSManagedObjectContext obtainPermanentIDsForObjects:error:], затем запустите приложение, активируйте его, перезагрузите, получите объект с помощью основного контекста objectWithID: и получите доступ к свойству, я получаю

    "CoreData не может выполнить ошибку для...".

Что не так с этим подходом?
4b9b3361

Ответ 1

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

[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:&error]

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

Как и когда вы звоните obtainPermanentIDsForObjects?

Я не следую за частью, связанной с отключением приложения. Возможно, лучшее объяснение поможет.

Ответ 2

Как сказал Джоди выше, при создании нового NSManagedObject в фоновом потоке с использованием дочернего ManagedObjectContext вы должны принудительно создать постоянный идентификатор, выполнив следующее до того, как вы сохраните:

NSError *error = nil;

[threadedMOC obtainPermanentIDsForObjects:threadedMOC.insertedObjects.allObjects error:&error];

BOOL success = [threadedMOC save:&error];

ИМХО, это не очень интуитивно, чтобы сделать это таким образом - ведь вы просите постоянный идентификатор до того, как вы сэкономите! Но так оно и работает. Если вы запрашиваете постоянный идентификатор после сохранения, то идентификатор будет по-прежнему временным. В Документах Apple вы можете использовать следующее, чтобы определить, является ли ID объекта временным:

BOOL isTemporary = [[managedObject objectID] isTemporaryID];

Ответ 3

Проблема все еще существует в iOS 8.3 Решение в Swift:

func saveContext(context: NSManagedObjectContext?){
   NSOperationQueue.mainQueue().addOperationWithBlock(){
    if let moc = context {
        var error : NSError? = nil
        if !moc.obtainPermanentIDsForObjects(Array(moc.insertedObjects), error: &error){
            println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)")
        }
        if moc.hasChanges && !moc.save(&error){
            println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)")
        }
    }
 }
}

func saveBackgroundContext(){
    saveContext(self.defaultContext)

    privateContext?.performBlock{
        var error : NSError? = nil
        if let context = self.privateContext {

            if context.hasChanges && !context.save(&error){
                println("\(__FUNCTION__)\n \(error?.localizedDescription)\n \(error?.userInfo)")
            }else {
                println("saved private context to disk")
            }
        }
    }
}

Где:

  • defaultContext имеет concurrencyType.MainQueueConcurrencyType
  • privateContext имеет concurrencyType.PrivateQueueConcurrencyType