webentwicklung-frage-antwort-db.com.de

Spezifisches iOS 12-Problem: Beschädigung der Binärdaten des externen Kerndatenspeichers

Ich habe den größten Teil eines Arbeitstages damit verbracht, dies zu lösen.

Hintergrund

Ich habe ein einfaches Kerndatenmodell mit Büchern und Lesesitzungen. Die Bücher haben Cover (Bilder), die als Binärdaten mit "Ermöglicht externes Speichern" gespeichert werden.

Unter iOS 11.4 und niedriger funktioniert alles immer einwandfrei. Wenn ich eine neue Sitzung speichere, wird alles korrekt aktualisiert.

Problem

Seit iOS 12, wenn ich eine neue Lesesitzung erstelle und sie mit dem Buch verknüpfe, generieren die Kerndaten zu jeder Sekunde Zeit eine SQL-Anweisung, die auch das Buchumschlagsfeld aktualisiert Eine fehlerhafte Referenz (auf eine Datei auf der Festplatte), die häufig dazu führt, dass das Cover beim Neustart der App nil ist und fast immer eine doppelte Kopie des Covers auf der Festplatte erstellt (wie in Simulators _EXTERNAL_DATA Ordner).

In-Memory-Kontext und -Objekte bleiben jedoch korrekt (und alles in der Benutzeroberfläche ist daher in Ordnung), bis die App neu gestartet wird, dann ist das Cover häufig nil.

iOS 12 spezifisch

Unter iOS 12 kann ich den Fehler im Simulator auf physischen Geräten deterministisch reproduzieren, und Benutzer haben den Fehler ebenfalls gemeldet. Ich kann den Fehler unter iOS 11.4 nicht reproduzieren, und kein Benutzer hat den Fehler vor iOS 12 gemeldet.

Schritte unternommen

  • Ich habe "-com.Apple.CoreData.ConcurrencyDebug 1" Aktiviert, daher sollte es nicht so sein, dass ich über die falsche Warteschlange auf etwas zugreife. Ich habe auch "-com.Apple.CoreData.SQLDebug 3" Aktiviert, damit ich genau sehen kann, was geschrieben wird.

  • Ich habe sichergestellt, dass die Buchinstanz (und daher das Cover) vor der Verknüpfung mit der neuen Sitzung nicht durch meinen Code geändert wird, indem ich hasChanges überprüfe, kurz bevor ich newSession.book = book Und context.save().

  • Um 100% sicher zu sein, dass ich die Cover-Eigenschaft in keinem Thread berühre, habe ich meine Getter und Setter für diese Eigenschaft kurzgeschlossen. Keine Verbesserung.

  • Ich habe versucht, mit objectID eine Instanz des Buches direkt vor der Zuordnung anzufordern und zu speichern. Keine Verbesserung.

  • Ich habe sogar die Option ausprobiert, bei der der Kontext starke Verweise auf alle Objekte enthält, um sicherzustellen, dass es sich nicht um ein Speicherverwaltungsproblem handelt. Keine Verbesserung.

Frage

Irgendwelche Ideen für die nächsten Schritte?

Statusaktualisierung

Dies ist ein Fehler in iOS 12. Eine ausführliche Beschreibung einer vernünftigen Problemumgehung finden Sie in der akzeptierten Antwort unten.

27
zendo

Update: Das zugrunde liegende Core Data-Problem scheint in iOS 12.1 behoben zu sein (verifiziert in Beta 4). Wir werden die unten beschriebene Problemumgehung in unserer App beibehalten und werden nicht empfehlen, in Kürze die Option Externer Speicher zu verwenden.


Nachdem wir mit Apple Technikern) gesprochen und das Radar oben erwähnt abgelegt hatten, konnten wir nicht auf eine Korrektur warten, also nahmen wir den Treffer und wechselten zum Speichern von Dateien auf dem Dateisystem und verwalten es direkt selbst.

Eine andere Alternative, die wir in Betracht gezogen haben, war die Migration unseres Modells, um externen Speicher für BLOBs nicht zuzulassen, aber ich weiß nicht, welche Auswirkungen dies auf die Leistung gehabt hätte, und ich war auch besorgt über eine Modellmigration zu einem Zeitpunkt, an dem dieser Teil von iOS zu sein scheint Seien Sie instabil, besonders nachdem Sie in der Vergangenheit Geschichten wie diese gelesen haben: Kerndaten: Speichern Sie keine großen Dateien als Binärdaten - Alexander Edge - Medium

Es war nicht allzu schmerzhaft, den lokalen Speicher selbst zu implementieren. Sie benötigen lediglich eine eindeutige Kennung für jeden Datensatz, mit der Sie einen Dateinamen erstellen können, damit Sie Dateien Datensätzen zuordnen können. Wir haben unserer Unterklasse für verwaltete Objekte eine Erweiterung mit Methoden zum Lesen, Schreiben und Löschen der Dateien hinzugefügt. Anstatt jetzt z. article.photo = image.pngData(), wir müssen jetzt etwas wie article.savePhoto(image.pngData()) aufrufen und dann machen wir es ähnlich, wenn wir das Bild abrufen wollen. Sie können diesen Methoden auch Code hinzufügen, um die Abwärtskompatibilität mit allen derzeit in Core Data gespeicherten Bildern zu unterstützen.

Das Löschen war etwas komplizierter, da unsere Objekte an mehreren Stellen im Code gelöscht werden, einschließlich kaskadierender Löschvorgänge. Am Ende habe ich mich für die Methode prepareForDeletion des verwalteten Objekts entschieden, aber es ist nicht ideal. Hier wird ausführlich darüber diskutiert, wie dies am besten implementiert werden kann: cocoa - Wie beim Löschen von ungespeicherten Core Data-Objekten mit der Bereinigung externer Daten umzugehen ist? - Stapelüberlauf

Um zu verhindern, dass unsere App abstürzt, wenn ein nicht-optionales Binärattribut aufgrund dieses Fehlers verschwunden ist, überschreibe ich awakeFromFetch in meiner Unterklasse für verwaltete Objekte, um sicherzustellen, dass alle erforderlichen Attribute nicht null sind, und wenn ja, I Setzen Sie sie auf ein Platzhalterbild, damit sie gespeichert werden können, ohne dass die Validierung fehlschlägt.

10
rodhan