Dieses Thema wurde in vielen Foren diskutiert, aber ich kann immer noch nicht vollständig verstehen, wie performBlockAndWait
tatsächlich funktioniert. Nach meinem Verständnis führt context.performBlockAndWait(block: () -> Void)
den Block in seiner eigenen Warteschlange aus, während er den Thread des Aufrufers blockiert. Dokumentation sagt Folgendes:
Sie gruppieren Standardnachrichten, die an den Kontext innerhalb eines Blocks gesendet werden sollen um zu einer dieser Methoden zu gelangen.
Was sind die "Standard" -Nachrichten? Es sagt auch:
Setter-Methoden in kontextbasierten verwalteten Objektkontexten sind Thread-sicher. Sie können diese Methoden direkt in einem beliebigen Thread aufrufen.
Bedeutet das, dass ich Eigenschaften eines verwalteten Objekts festlegen kann, das innerhalb der performBlock
* API eines Kontexts außerhalb von performBlock
* APIs abgerufen wird?
Nach meinem Verständnis wird das Aufrufen von performBlockAndWait(block: () -> Void)
für den Kontext mit dem Parallelitätstyp .MainQueueConcurrencyType
einen Deadlock erzeugen und die Benutzeroberfläche für immer blockieren, wenn sie vom Hauptthread aufgerufen wird. Aber in meinen Tests erzeugt es keinen Deadlock.
Der Grund, warum ich denke, dass es einen Deadlock erzeugen sollte, ist, dass performBlockAndWait zuerst den aufrufenden Thread blockiert und dann den Block auf seinem eigenen Thread ausführt. Da der Thread, in dem der Kontext seinen Block ausführen muss, derselbe wie der bereits blockierte Thread des Aufrufers ist, wird er seinen Block nie ausführen können und der Thread bleibt für immer blockiert.
Ich stehe jedoch in einem seltsamen Szenario vor Deadlocks. Ich habe unter Testcode:
%Vor% Beachten Sie, dass ich versehentlich performBlockAndWait
zweimal in fetchDepartment
method in meinen Testcode eingefügt habe.
fetchAllStudentsOfDepartment
Methode. Aber sobald ich anrufe
fetchAllStudentsOfDepartment
, jeder Aufruf von fetchDepartment
Methode
blockiert die Benutzeroberfläche für immer. print(student.firstName)
in der Methode fetchAllStudentsOfDepartment
lösche, wird sie nicht blockiert. Das heißt, es blockiert die Benutzeroberfläche nur, wenn ich auf die Eigenschaft einer Beziehung zugreife. privateContext
hat concurrencyType
auf .PrivateQueueConcurrencyType
gesetzt. Der obige Code blockiert die Benutzeroberfläche nur dann, wenn privateContext
s parentContext
concurrencyType
auf .MainQueueConcurrencyType
gesetzt hat.
Ich habe den gleichen Code auch mit anderen .xcdatamodel
getestet und ich bin mir sicher, dass er nur blockiert, wenn auf die Eigenschaft einer Beziehung zugegriffen wird. Mein aktuelles .xcdatamodel
sieht folgendermaßen aus:
Verzeihen Sie mir, wenn die Information fremd ist, aber ich teile nur alle meine Beobachtungen, nachdem ich bereits 8 Stunden verbracht habe. Ich kann meinen Thread-Stack veröffentlichen, wenn die Benutzeroberfläche blockiert ist. Zusammenfassend habe ich drei Fragen:
performBlock
* API eines Kontextes außerhalb von performBlock
*? performBlockAndWait
sich falsch verhält und in meinem Testcode einen UI-Block verursacht. TESTCODE: Sie können den Testcode von hier
Standardnachrichten sind alte Objective-C-Ausdrücke. Das bedeutet, dass Sie alle regulären Methodenaufrufe für einen ManagedObjectContext und dessen untergeordnete ManagedObjects in performBlock
oder performBlockAndWait
ausführen sollten. Die einzigen Aufrufe, die für einen privaten Kontext außerhalb des Blocks zulässig sind, sind init
und setParentContext
. Alles andere sollte in einem Block gemacht werden.
Nein. Auf jedes verwaltete Objekt, das aus einem privaten Kontext abgerufen wird, darf nur in der Warteschlange dieses privaten Kontexts zugegriffen werden. Der Zugriff auf (Lesen oder Schreiben) aus einer anderen Warteschlange verstößt gegen die Thread-Einschränkungsregeln.
Der Grund dafür, dass Sie Probleme beim Sperren haben, ist, dass Sie zwei Ebenen von "mainQueue" -Kontexten haben und das Warteschlangensystem "überlisten". Dies ist der Ablauf:
Wegen der zwei Ebenen der Hauptwarteschlangenkontexte verursacht es einen Deadlock, wo normalerweise das Warteschlangensystem den potentiellen Deadlock sehen und vermeiden würde.
Sie können dies testen, indem Sie die Variable mainContext
in
Und Ihr Problem verschwindet, weil das Warteschlangensystem den Block sehen und vermeiden wird. Sie können dies sogar sehen, indem Sie innerhalb von performBlockAndWait()
einen Unterbrechungspunkt setzen und sehen, dass Sie sich immer noch in der Hauptwarteschlange befinden.
Am Ende gibt es keinen Grund, zwei Ebenen von Hauptwarteschlangenkontexten wie in einem Eltern / Kind-Design zu haben. Wenn überhaupt, ist dies ein gutes Argument, das NICHT zu tun.
Ich habe übersehen, dass Sie den Vorlagencode in appDelegate geändert und den Gesamtkontext in einen privaten umgewandelt haben.
Dieses Muster, einen Haupt-MOC pro Vc zu haben, wirft viele Vorteile von Core Data weg. Während ein Privates an der Spitze und ein Haupt-MOC (das für die gesamte App existiert, nicht nur ein VC) ein gültiges Design ist, wird es nicht funktionieren, wenn Sie performBlockAndWait
so aus der Hauptwarteschlange machen.
Ich würde nicht empfehlen, jemals performBlockAndWait
aus der Hauptwarteschlange zu verwenden, da Sie die gesamte Anwendung blockieren. performBlockAndWait
sollte immer nur verwendet werden, wenn TO die Hauptwarteschlange (oder vielleicht ein Hintergrund zu einem anderen Hintergrund) aufgerufen wird.
- Was sind die "Standard" -Nachrichten?
Jede Nachricht, die an den verwalteten Objektkontext oder an ein beliebiges verwaltetes Objekt gesendet wird. Beachten Sie, dass die Dokumentation weiterhin klarstellt ...
%Vor% Somit muss alles außer einer Setter-Methode auf einem MOC von innerhalb eines performBlock
aufgerufen werden. Jede Methode auf einem MOC mit dem Namen NSMainQueueConcurrencyType
kann vom Hauptthread aufgerufen werden, ohne in ein performBlock
eingeschlossen zu werden.
- Können wir Eigenschaften eines verwalteten Objekts festlegen, das innerhalb der performBlock * API eines Kontexts außerhalb von performBlock *?
abgerufen wird?
Nein. Jeder Zugriff auf ein verwaltetes Objekt muss im Kontext von performBlock
im Kontext des verwalteten Objekts, in dem sich das verwaltete Objekt befindet, geschützt sein. Beachten Sie die Ausnahme für verwaltete Objekte, die sich in einer MOC-Hauptwarteschlange befinden, auf die von der Hauptwarteschlange zugegriffen wird.
- Warum performBlockAndWait fehlerhaft ist und einen UI-Block in meinem Testcode verursacht.
Es ist nicht schlecht benehmen. performBlockAndWait
ist reentrant, aber nur, wenn bereits ein performBlock[AndWait]
-Aufruf durchgeführt wird.
Sie sollten niemals performBlockAndWait
verwenden, es sei denn, Sie haben keine andere Option. Dies ist besonders bei verschachtelten Kontexten problematisch.
Verwenden Sie stattdessen performBlock
.
Tags und Links multithreading ios core-data swift nsmanagedobjectcontext