17

Wir haben versucht, ein Core Data Multiple Context/Threading-Problem zu debuggen, bei dem das Zusammenführen einer Core Data-Save-Benachrichtigung in unserem Hauptthread NSManagedObjectContext sporadisch die App abstürzt. Das stürzt ~ 2% unserer App-Sitzungen ab und wir wissen nicht, wie wir das lösen können. Wir würden uns über jegliche Hinweise oder allgemeinen Ratschläge, die möglicherweise zu diesem Absturz führen könnten, sehr freuen.EXC_BAD_ACCESS on mergeChangesFromContextDidSaveNotification

Wir haben ein Core Data-Setup, das wie folgt aussieht:

Core Data Stack N. B. Dies ist der Standard Core Data-Stack in magischen Rekord v2.3 von erstellt [MagicalRecord setupAutoMigratingCoreDataStack]

Dies ist das Szenario, in dem unsere App abstürzt: ist

  1. HTTP-Anforderung zurückgibt JSON
  2. JSON analysiert in NSManagedObject s (einige neue Einheiten, einige aktualisierte Entitäten) Kontext auf Root-Saving
  3. Root-Saving Context zu persistenten Speicher speichert
  4. NSManagedObjectContextDidSaveNotification wird von Core Data gesendet. Der Standardkontext in der Hauptwarteschlange beobachtet dies und ruft mergeChangesFromContextDidSaveNotification: mit der NSDictionary von Änderungen am Hauptthread auf.
  5. Es stürzt ab, wenn objectID an ein ungültiges Objekt gesendet wird (am wahrscheinlichsten wurde freigegeben).

Dies ist innerhalb der privaten Implementierung von NSManagedObjectContext auftretenden mergeChangesFromContextDidSaveNotification: so ist es für uns unmöglich, zu sehen, was eigentlich falsch hier gegangen ist; Alles, was wir an dieser Stelle sagen können, ist, dass ein Objekt, das existieren sollte, dies nicht tut.

enter image description here

Dies ist nur auf einem kleinen Prozentsatz von Core Data geschieht spart, was darauf hinweist, dass nicht ein grundlegender Fehler in unserem Core Data → API-Stack sein kann. Darüber hinaus gibt es keinen Hinweis darauf, dass die Größe oder der Typ der Änderungen (Einfügungen/Aktualisierungen/Löschungen) in den Kontextänderungen irgendeinen Einfluss auf die Wahrscheinlichkeit des Absturzes haben.

+1

Welche Art von Nebenläufigkeit verwenden diese MOCs? Und verwenden Sie 'performBlock:' oder 'performBlockAndWait:' beim Zusammenführen von Änderungen? –

+0

Das Standard-MoC verwendet den Hauptwarteschlangen-Parallelitätstyp und das Root-Speicher-MoC verwendet den Parallelitätstyp "Private Warteschlange". Jede Interaktion mit privaten Warteschlangen in der App verwendet performBlock oder performBlockAndWait. Wie in der Apple-Dokumentation beschrieben, interagieren wir nicht mit dem Default-Kontext durch die Ausführung von block, wir stellen jedoch sicher, dass wir niemals mit ihm interagieren, wenn wir nicht im Hauptthread sind. – JConway

+4

Nur meine 2ct; Ich hatte einmal ein solches Problem, während ich alles nach dem Buch durchführte, und es wurde von einem 'NSLog()' des 'NSNotification'-Objekts (oder der 'userInfo'-Eigenschaft davon) verursacht. Seither mache ich nur 'mergeChangesFromContextDidSaveNotification:' unmittelbar gefolgt von 'save' im Kontext, ohne die Benachrichtigung auf andere Weise zu berühren. Seitdem habe ich es nie wieder segfault gesehen. – mvds

Antwort

1

Es ist schon eine Weile her, seit diese Frage gestellt wurde und nachdem ich sie wieder entdeckt habe, möchte ich meine eigene Frage beantworten, um anderen zu helfen, die diesen Thread finden.

In meinem Fall hatte ich eine große Codebasis von Geschwister NSManagedObjectContexts aktualisiert über NSManagedObjectContextDidSaveNotification 's migriert. Allerdings hatte das Problem nicht wirklich etwas damit zu tun, obwohl dies das Problem aufdeckte.

Die wirkliche Ursache dafür waren Orte, an denen ältere Teile des Codes, von früheren Ingenieuren eingerichtet, die KVO auf NSManagedObject s und ihre Eigenschaften eingerichtet hatten. Es stellte sich heraus, dass KVO auf Core Data Entities in der Tat eine sehr sehr schlechte Idee ist.

Genauer schien es, dass dies passiert, wenn KVO auf Entitäten eingerichtet wurde und entweder das Objekt oder das Ziel einer Beziehung auf diesem Objekt aus der gelöscht wurde. Diese zweite Bedingung schien nicht die Ursache des Problems zu sein, aber war definitiv eine sehr prominente Ursache in meiner Situation.

Lektion gelernt hat:

  1. Verwenden Sie einen abgerufenen Ergebnisse Controller, wenn Sie benötigen. KVO ist keine bequeme Abkürzung, und Sie sollten nicht vermeiden, den dubiosen Core Data KVO-Code in NSFetchedResultsControllers oder eine andere sinnvolle Alternative zu migrieren, da das Aufschieben Sie nur verletzen wird.
  2. Multi-Thread-Core-Daten ist eine schwierige, aber sehr lohnende Fähigkeit, ein Experte in. Sein Core Data Stack und die Nuancen und Grenzen der Core Data Multithreading ist absolut alle seelischen Qualen wert.
4

Die Dokumentation von NSManagedObjectContextDidSaveNotification sagt, dass: direkt auf einem anderen Thread

„Sie haben die Meldung Objekt mergeChangesFromContextDidSaveNotification: auf einem anderen Thread passieren können, müssen Sie jedoch nicht das verwaltete Objekt im Wörterbuch Benutzer Informationen verwenden Weitere Einzelheiten. Siehe Parallelität mit Stammdaten in der Core Data-Programmieranleitung. "

Vielleicht ist das das Problem? Ich würde sicherstellen, dass das Objekt, das Sie von der Benachrichtigung erhalten, im Standardkontext für denselben Thread gespeichert wird, den es von Root gepostet hat.

+0

Das war genau mein Problem. Ich habe vergessen zu versenden, indem ich 'performBlock' für ManagedObjectContext aufruft. – TMob

2

Eine Möglichkeit ist, dass Ihr persistenter Speicher beschädigt wurde und sich in einem inkonsistenten Zustand befindet. In diesem Fall wird ein Fehlercode generiert, den Magical Record nicht unbedingt behandelt. Dies kann die Ursache für eine Reihe von schwer zu wiederholenden scheinbar zufälligen Abstürzen sein, die mit Magical Record zusammenhängen (und möglicherweise nicht als Magical Record Bug betrachtet werden).

Es lohnt sich, die Magical Record Ausgaben Themen here (selbe Ausgabe) und here (anderes Problem, könnte aber ähnliche Ursache sein) zu lesen. Als ich auf diese Probleme stieß, konnte ich nach einigen Hinweisen in diesen Threads einige temporäre Korrekturen vornehmen, aber letztendlich entschied ich mich, meine Abhängigkeit von Magical Record zu entfernen, und seitdem hatte ich keine Probleme mehr.