Ich versuche, Funktionen mit dem Parameter inout
einzufügen, um Daten, die von asynchronen Callbacks empfangen werden, an ein externes Array anzufügen. Es funktioniert jedoch nicht. Und ich habe alles versucht, um herauszufinden, warum - ohne Glück.
Wie von @AirspeedVelocity empfohlen, habe ich den Code wie folgt neu geschrieben, um unnötige Abhängigkeiten zu entfernen. Ich verwende auch eine Int
als inout
-Parameter, um es einfach zu halten.
Die Ausgabe ist immer:
c before: 0
c after: 1
Ich bin nicht in der Lage herauszufinden, was hier falsch läuft.
%Vor%Danke.
Leider ist die Änderung des Parameters inout
in async-callback bedeutungslos.
Aus dem offiziellen Dokument :
Parameter können Standardwerte zur Vereinfachung von Funktionsaufrufen bereitstellen und als In-Out-Parameter übergeben werden, die eine übergebene Variable ändern, sobald die Funktion ihre Ausführung abgeschlossen hat .
...
Ein In-Out-Parameter hat einen Wert, der an die Funktion übergeben wird, von der Funktion geändert wird und aus der Funktion zurückgegeben wird, um den ursprünglichen Wert zu ersetzen.
Semantisch ist der In-Out-Parameter nicht "Call-by-Reference" , sondern "call-by-copy-restore" .
In Ihrem Fall wird counter
nur dann schreibgeschützt, wenn getOneUserApiData()
zurückgibt, nicht in dataTaskWithRequest()
callback.
Hier ist, was in Ihrem Code passiert ist
getOneUserApiData()
Aufruf, der Wert von counter
0
kopiert nach c
1
c
1
dataTaskWithRequest()
getOneUserApiData
gibt zurück, und der Wert von - unmodified - c
1 wird in counter
schreibgeschützt
c
2 , c
3 , c
4 ... c
1 wird inkrementiert. c
2 wird inkrementiert. c
3 wird inkrementiert. c
4 wird inkrementiert. Als Ergebnis ist counter
unverändert: (
Detaillierte Erklärung
Normalerweise wird in-out
-Parameter als Referenz übergeben, aber es ist nur ein Ergebnis der Compiler-Optimierung. Wenn closure inout
-Parameter erfasst, ist "pass-by-reference" nicht sicher , da der Compiler die Lebensdauer des ursprünglichen Werts nicht garantieren kann. Betrachten Sie beispielsweise den folgenden Code:
In diesem Code wird var i
freigegeben, wenn foo()
zurückgibt. Wenn x
eine Referenz auf i
ist, verursacht x++
Zugriffsverletzung. Um solche Wettlaufzustände zu verhindern, wendet Swift hier eine "Call-by-Copy-Restore" -Strategie an.
Im Wesentlichen sieht es so aus, als ob Sie versuchen, die "Inout-Ness" einer Eingabevariablen in einem Closure zu erfassen, und Sie können das nicht tun - betrachten Sie den folgenden einfacheren Fall:
%Vor% An einem bestimmten Punkt hört die übergebene Variable auf, x
zu sein und wird zu einer Kopie. Dies geschieht wahrscheinlich zum Zeitpunkt der Erfassung.
edit: @ rintaros Antwort nagelt es - inout
wird nicht semantisch als Referenz übergeben
Wenn Sie darüber nachdenken, macht das Sinn. Was, wenn Sie das getan haben:
%Vor% Wenn Closures Variablen erfassen, müssen sie im Speicher so erstellt werden, dass sie auch dann noch am Leben bleiben, wenn der Bereich, für den sie deklariert wurden, endet. Aber im Fall von f
oben kann es das nicht tun - es ist zu spät, x
auf diese Weise zu deklarieren, x
existiert bereits. Also ich vermute, dass es als Teil der Schließung Schöpfung kopiert wird. Aus diesem Grund erhöht die Erhöhung der Closure-Capture-Version nicht wirklich x
.
Ich hatte ein ähnliches Ziel und stieß auf das gleiche Problem, bei dem die Ergebnisse innerhalb der Closure meinen globalen inout-Variablen nicht zugewiesen wurden. @rintaro hat sehr gut erklärt, warum dies in einer früheren Antwort der Fall ist.
Ich werde hier ein verallgemeinertes Beispiel vorstellen, wie ich daran gearbeitet habe. In meinem Fall hatte ich mehrere globale Arrays, die ich innerhalb eines Closings zuweisen wollte, und dann jedes Mal etwas (ohne einen Haufen Code zu duplizieren).
%Vor%@rintaro hat perfekt erklärt, warum es nicht funktioniert, aber wenn Sie das wirklich wollen, wird die Verwendung von UnsafeMutablePointer den Trick machen:
%Vor%Tags und Links swift