In meiner App schreibe ich regelmäßig eine Reihe von dynamischen Daten in die Datei. Das Datenobjekt wird ungefähr jede Sekunde aktualisiert. Gelegentlich bekomme ich eine Ausnahme "Sammlung wurde mutiert, während sie mutiert ist" in einer der Zeilen in meiner encodeWithCoder: -Methode. Jedes Objekt ist wie folgt codiert:
%Vor%Wo self.speeds ein NSMutableArray ist. Ich nehme an, das Problem besteht darin, dass die Daten während der Codierung aktualisiert werden. Ich habe versucht @synchronize in die Codierung und Speichern von Blöcken und ich habe auch versucht, die Eigenschaft atomare im Gegensatz zu nichtatomaren, aber keiner funktionierte. Das Speichern wird im Hintergrund ausgeführt. Irgendwelche Ideen, wie Sie diese Daten im Hintergrund speichern können, während sie aktualisiert werden? Ich habe das Gefühl, eine Kopie zu machen und dann die Kopie zu speichern würde funktionieren, aber würde nicht das gleiche Problem entstehen? Danke!
Die Idee in der App ist, dass ich eine Kartenansicht öffne, die regelmäßig eine Singleton-Klasse mit einem Array von Datenobjekten aktualisiert, wobei jedes Datenobjekt eine Karteninformation eines Benutzers ist. In jedem Datenobjekt werden die Standorte, Geschwindigkeiten, Höhen, Entfernungen usw. des Benutzers angezeigt. Jedes Mal, wenn der Standortmanager den Standort des Benutzers aktualisiert, aktualisiere ich das aktuelle Datenobjekt (das Live-Datenobjekt, das gerade erstellt wurde, um diese Reise zu verfolgen). es kann zu jeder Zeit nur ein "Live" -Datenobjekt mit der neuen Information geben.
Ich möchte den gesamten Singleton alle paar Minuten in eine Datei schreiben, aber manchmal kommt das Schreiben und das Update zur gleichen Zeit und ich bekomme diesen Fehler (oder zumindest das ist es, was ich nehme an, verursacht diesen Absturz). Ist das Problem hier mit meinem Code oder meinem Designmuster?
Dies ist die Codierungsmethode in meiner benutzerdefinierten Klasse:
%Vor% Dies ist die Update-Methode (es gibt mehr Code in der Methode und anderen Methoden, die in der update-Methode aufgerufen werden, aber ich lasse alles weg, was nicht auf das 'live' dataObject
-Objekt verweist; das eine wird aktualisiert) :
Und schließlich die synchronize
-Methode in der SingletonDataController
-Klasse (so aktualisiert, dass jetzt die Synchronisation innerhalb des asynchronen Blocks gemäß Tommys Antwort stattfindet):
wo backgroundQueue wie folgt erstellt wird:
%Vor%Ich kann bei Bedarf mehr Code posten, aber das scheinen die wichtigen Teile zu sein.
Sie führen dispatch_async
in einem Ihrer @synchronize
s aus. Das Zeug dort ist nicht Gegenstand der impliziten Sperre, die in die Synchronisation eingebaut ist; alles, was passiert, dass du das Schloss bekommst, den Block losschickst und dann das Schloss loslässt. So kann der Block leicht außerhalb des Schlosses passieren (und tatsächlich würden Sie erwarten, dass dies normalerweise geschieht).
Um beim Synchronisationspfad zu bleiben, möchten Sie @synchronize
innerhalb des Blocks und nicht außerhalb davon. Sie könnten jedoch versuchen, einen weniger eindringlichen Ansatz zu finden, z. B. alle Aktualisierungen in einer einzigen seriellen Dispatch-Warteschlange auszuführen und ihnen zu ermöglichen, relevante neue Werte in die Hauptwarteschlange zu übernehmen.
Wenn Sie befürchten, dass die Serialisierung lange genug dauert, um die nächste Serialisierung zu beeinflussen, kopieren Sie das Objekt und verwenden Sie dann dispatch_async
, um es zu serialisieren. Auf diese Weise findet die Serialisierung in einer asynchronen Warteschlange statt.
Vielleicht möchten Sie diesen Ansatz jedoch vollständig überdenken. Ist Core Data keine Option? Damit könnten Sie nur die Werte aktualisieren, die sich tatsächlich geändert haben, und ich bin mir ziemlich sicher, dass es Ihre Locking-Probleme behandeln kann.
Bearbeiten Entschuldigen Sie, dass ich Ihren ursprünglichen Beitrag falsch gelesen habe. Wenn Sie nicht zu oft speichern, sollten Sie die Verwendung von Sperren in Betracht ziehen. Siehe Ссылка
Aber tue das nur, wenn du nicht zu oft serialisierst, da dies deine Leistung erheblich verringert.
Sperren Sie also das Objekt, kopieren Sie es, entsperren Sie das Objekt, kopieren Sie die Kopie asynchron.
Ja, das Serialisieren einer Kopie des Arrays anstelle des veränderbaren Arrays stellt sicher, dass sich das Array während des Speicherns nicht ändert, aber Sie verschieben lediglich das Problem: Sie haben immer noch den Fall, dass sich das Array ändern könnte ein Thread, während es auf der anderen Seite kopiert wird. Sie können @synchronize-Blöcke um die Kopier- und die Array-Mutation setzen (genau wie Sie sagten, dass Sie mit dem Speichern / Aktualisieren gearbeitet haben ... das sollte funktioniert haben - wenn Sie das gleiche Objekt für das @ verwenden Parameter synchronisieren? @synchronize (self) ist ein praktischer Weg, dies zu tun.)
Eine weitere Möglichkeit, den Kopiervorgang zu synchronisieren, ist die Verwendung von dispatch_sync (), um die Kopie im Hauptthread zu erstellen:
%Vor%Dies ist etwas grobkörniger - es kann die Kopie erst erstellen, wenn der Hauptthread leer ist, während die @ synchronisierte Kopie ausgeführt werden kann, sobald der Hauptthread außerhalb des @ synchronize-Blocks ist - aber es hat Der Vorteil, dass Sie diesen Code nur in den Sicherungs-Thread einfügen müssen und sich nicht darum kümmern müssen, wo Sie das Array im Haupt-Thread ändern könnten.
Bearbeiten: Habe gerade den anderen Hinweis zur Verwendung von NSLock gesehen. Die Verwendung von @synchronize ist in etwa dasselbe wie die Verwendung eines NSLock ( hier ist ein guter SO-Post darüber, aber Sie müssen sich keine Sorgen über die Verwaltung eines Sperrobjekts machen. Auch hier sollte @synchronize für Sie arbeiten, und es ist wirklich praktisch, solange Sie nicht Dutzende von verschiedenen Orten haben, die Sie synchronisieren müssen.
Tags und Links objective-c ios serialization asynchronous nscoder