Базовые данные: получают ли когда-либо дочерние контексты постоянные objectID для вновь вставленных объектов?

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

  • Родительский контекст: 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: и получить доступ к собственности, я получаю

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

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

3 ответа

Решение

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

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

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

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

Я не слежу за частью о сбое приложения. Может быть, поможет лучшее объяснение.

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

NSError *error = nil;

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

BOOL success = [threadedMOC save:&error];

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

BOOL isTemporary = [[managedObject objectID] isTemporaryID];

Проблема все еще существует в 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
Другие вопросы по тегам