Der Inout-Parameter im asynchronen Callback funktioniert nicht wie erwartet

7

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.

    
LiweiZ 31.01.2015, 00:49
quelle

4 Antworten

16

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

  1. bei getOneUserApiData() Aufruf, der Wert von counter 0 kopiert nach c 1
  2. Der Abschluss erfasst c 1
  3. Aufruf dataTaskWithRequest()
  4. getOneUserApiData gibt zurück, und der Wert von - unmodified - c 1 wird in counter schreibgeschützt
  5. Wiederholen Sie die 1-4 Prozedur für c 2 , c 3 , c 4 ...
  6. ... aus dem Internet holen ...
  7. Callback wird aufgerufen und c 1 wird inkrementiert.
  8. Callback wird aufgerufen und c 2 wird inkrementiert.
  9. Callback wird aufgerufen und c 3 wird inkrementiert.
  10. Callback wird aufgerufen und c 4 wird inkrementiert.
  11. ...

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:

%Vor%

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.

    
rintaro 31.01.2015, 13:26
quelle
6

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 .

    
Airspeed Velocity 31.01.2015 13:33
quelle
0

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%     
Michael Peterson 19.03.2016 05:15
quelle
0

@rintaro hat perfekt erklärt, warum es nicht funktioniert, aber wenn Sie das wirklich wollen, wird die Verwendung von UnsafeMutablePointer den Trick machen:

%Vor%     
quelle

Tags und Links