2012-08-16 6 views
27

Ich habe eine App mit zwei verwalteten Objektkontexten Setup wie folgt aus:Core Data: Erhalten Child-Kontexte jemals permanente ObjectIDs für neu eingefügte Objekte?

  • Eltern Kontext: NSPrivateQueueConcurrencyType, zu dem persistenten Speicher verbunden.
  • Hauptkontext: NSMainQueueConcurrencyType, untergeordnetes Element des übergeordneten Kontexts.

Wenn ein neues verwaltetes Objekt in den Haupt Kontext einzufügen, ich spare den Haupt-Kontext und dann die Elternkontext wie folgt aus:

[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]); 
    } 
}]; 

Mein Verständnis ist, dass, wenn das Objekt verwalten wird es zuerst erstellt, hat eine temporäre objectID. Dann wird der Hauptkontext gespeichert und dieses Objekt mit seiner temporären ID gelangt in den übergeordneten Kontext. Dann wird der übergeordnete Kontext gespeichert. Wenn dieser letzte Kontext gespeichert wird, wird das temporäre objectID im übergeordneten Kontext in eine permanente objectID umgewandelt.

So:

  • Hat die permanente Objekt-ID immer automatisch zurück den main (Kind) Kontext propagiert werden?
  • Wenn ich zwingen, das Objekt permanent ID mit [NSManagedObjectContext obtainPermanentIDsForObjects:error:], dann Hintergrund der App zu bekommen, reaktivieren, neu zu laden, erhalten Sie das Objekt objectWithID: Hauptkontext verwendet wird, und eine Eigenschaft zuzugreifen, ich

    „Coredata bekommen könnte keinen Fehler für ... erfüllen ".

Was ist falsch an diesem Ansatz?

+0

Jorge: Ich bekomme eine CoreData konnte einen Fehler nicht erfüllen, wenn Sie eine permanente ID in einem Kindkontext erhalten und ManagedObject speichern. Hast du herausgefunden, was der Grund war? Danke –

Antwort

40

Es ist ein bekannter Fehler, hoffentlich bald behoben, aber im Allgemeinen ist eine permanente ID zu erhalten ausreichend, Sie so zur Verfügung gestellt, bevor Sie die Daten in dem ersten Kind zu retten, und Sie sind nur die eingefügten Objekte:

In einigen komplexen Fällen ist es besser, eine permanente ID zu erhalten, sobald Sie die Instanz erstellen, insbesondere wenn Sie komplexe Beziehungen haben.

Wie und wann rufen Sie obtainPermanentIDsForObjects?

Ich folge nicht den Teil über die App abstürzt. Vielleicht würde eine bessere Erklärung helfen.

+0

Es gibt nichts in moc.insertedObjects gesetzt. Ich denke das wird nach dem Speichern gelöscht? Was ich mache, speichert die Benutzerauswahl von Dokumenten, wenn die App im Hintergrund ist. Die App kann diese aktualisieren, während sie sich im Hintergrund befindet. Wenn es in den Vordergrund tritt, wird der Kontext neu erstellt und die Auswahl wird mithilfe der Objekt-IDs wiederhergestellt. Bevor sie in den Hintergrund treten, haben die neu erstellten Dokumente jedoch auch nach dem Speichern des Kontexts temporäre IDs. Wenn ich erzwinge, die permanenten IDs für diese zu bekommen, wenn die App reaktiviert wird, kann sie die verwalteten Objekte nicht neu erstellen. – Jorge

+1

Ja, insertObjects enthält nur Objekte, die eingefügt und nicht gespeichert wurden. Auch nachdem Sie ihnen permanente IDs gegeben haben, bleiben sie in insertObjects, bis sie gespeichert werden. Versuchen Sie, vor dem Speichern den Befehl in Ihrem Kindkontext zu erhalten. Es sollte die IDs abfangen und dann das Speichern basierend darauf propagieren. Stellen Sie sicher, dass Sie über alle Kontexte vollständig in der Datenbank speichern. –

+0

Das Erhalten der IDs kurz vor dem Speichern hat den Trick gemacht. Vielen Dank! – Jorge

8

Als Jody oben gesagt, wenn eine neue NSManagedObject in einem Hintergrundthread mit einem Kind zu schaffen ManagedObjectContext Sie die Schaffung einer permanenten ID, indem Sie die folgenden zwingen müssen, bevor Sie speichern:

NSError *error = nil; 

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

BOOL success = [threadedMOC save:&error]; 

IMHO, ist es nicht wirklich intuitiv, um es auf diese Weise zu tun - schließlich bitten Sie um eine dauerhafte Identifikation, BEVOR Sie speichern! Aber so scheint es zu funktionieren. Wenn Sie nach dem Speichern nach der permanenten ID fragen, ist die ID weiterhin temporär.Von dem Apple-Docs, können Sie tatsächlich die folgende verwenden, um festzustellen, ob die ID des Objekts ist zeitlich begrenzt:

BOOL isTemporary = [[managedObject objectID] isTemporaryID]; 
1

Problem existiert noch in iOS 8.3 Lösung in 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") 
      } 
     } 
    } 
} 

Wo:

  • defaultContext hat ConcurrencyType .MainQueueConcurrencyType
  • privateContext hat concurrencyType .PrivateQueueConcurrencyType