Konstante String-Parameter und der Delphi XE5-Compiler für Android

8

Ich verpasse nur hoffentlich etwas Offensichtliches, aber ich finde, dass konstante String-Argumente bei Verwendung des Delphi XE5 Android-Compilers beschädigt werden. Testcode:

1) Erstellen Sie ein neues leeres mobiles Anwendungsprojekt.

2) Fügen Sie dem Formular einen TButton hinzu und erstellen Sie einen OnClick -Handler dafür.

3) Füllen Sie den Handler wie folgt aus:

%Vor%

4) Fügen Sie in der Formularklassendeklaration zwei Felder und eine Methode wie folgt hinzu:

%Vor%

5) Implementieren Sie Foo und GoToDirectory so:

%Vor%

6) Kompilieren und auf einem Gerät ausführen.

Wenn ich dies tue, zeigen die ersten zwei Meldungsfelder nichts falsch an, jedoch wird Dir dann zwischen der dritten und vierten Eingabeaufforderung gelöscht. Bekommt jemand anderes das, oder mache ich nur etwas Dummes? (Es gibt nichts Unerwartetes, wenn ich Win32 zu Testzwecken targetiere.)

Aktualisieren

Erstellen Sie für eine FMX-freie Version erneut eine neue leere mobile Anwendung, aber entfernen Sie dieses Mal das Formular aus dem Projekt. Gehen Sie dann in die Projektquelle und fügen Sie den folgenden Code hinzu:

%Vor%

Um das Ergebnis zu sehen, führen Sie zuerst monitor.bat im Android SDK tools -Ordner aus; um das Holz durch die Bäume zu sehen, filtern Sie nur nach Fehlern, wenn ich LOGE Anrufe verwendet habe. Während nicht jedes Mal, wenn ich diese überarbeitete Test-App ausführen, das Argument wird beschädigt, es ist immer noch manchmal ... was auf einen ziemlich bösen Compiler-Bug hindeutet ...

Update 2

Besonders mit dem zweiten Testfall überzeug ich mich noch mehr, also habe ich es als angemeldet QC 121312 .

Update 3

Ein Code und keine Prosaversion der Erklärung in der untenstehenden Antwort (Schnittstellentypen, die im Wesentlichen den gleichen Referenzzählmechanismus wie Strings verwenden, nur mit der Fähigkeit, leicht zu verfolgen, wenn das Objekt zerstört wird):

%Vor%

Die Ausgabe ist dies:

%Vor%

Wenn Sie also das Feld neu zuweisen, wird die Referenzzahl des vorherigen Objekts gelöscht. Wenn keine andere starke Referenz darauf vorhanden ist, wird die Referenznummer gelöscht, bevor die Prozedur ChangeCanary beendet ist.

    
Chris Rolliston 22.12.2013, 17:36
quelle

4 Antworten

5

Wir haben einige interne Untersuchungen durchgeführt, und es stellt sich heraus, dass dies von der Art abhängt, wie der Code geschrieben wird, und dass der Compiler nichts dagegen tun kann. Es ist etwas komplex, aber kurz gesagt, die GoToDirectory-Methode erhält einen konstanten String-Parameter (Dir), der sich auf einen String bezieht. Im Code der Methode wird jedoch die Zeichenfolge durch eine neue Zeichenfolge ersetzt (die sich möglicherweise an derselben Stelle oder an einem anderen Speicherort befindet). Wenn der const-Parameter die Referenzzahl nicht erhöht, wird die Zeichenfolge entfernt, wenn Sie die Ref-Anzahl der gleichen Zeichenfolge im Code verringern. Sie haben also einen Parameter, der auf einen nicht definierten Speicherplatz zeigt, und die tatsächliche Ausgabe ist zufällig. Das gleiche Problem tritt auf allen Plattformen auf (nicht für Mobilgeräte).

Es gibt viele Problemumgehungen:

1) nicht den const-Parameter haben (daher ist die ref-Zählung höher, Sie ändern die referenzierte Zeichenfolge, aber der Parameter ist jetzt eine Referenz auf eine separate Zeichenfolge

)

2) Übergeben Sie einen Alias ​​der Zeichenfolge:

%Vor%

3) Weisen Sie der temporären lokalen Variablen den Parameter "const String" zu:

%Vor%

Ich weiß, dass dies alles andere als eine klare Beschreibung ist, und ich musste es ein paar Mal rot machen, um es zu verstehen, aber es ist ein Szenario, das der Compiler nicht wirklich automatisch verarbeiten kann ".

    
Marco Cantù 09.01.2014, 10:27
quelle
4

Um Marcos Kommentar ein wenig zu erweitern, ist dieser Grubenabfall bei der Verwendung von const auf einem Parameter in Delphi seit der Einführung von const -Parametern aufgetreten und ist kein Bug, sondern eher ein Feature, das Ihre Beispiel ist ein Beispiel für einen Fall, es sollte nicht verwendet werden.

Der Modifikator const ist ein Versprechen an den Aufrufer, dass es keine Möglichkeit gibt, dass eine als Parameter übergebene Variable als Nebeneffekt des Aufrufs modifiziert wird. Der einfachste Weg, dies zu gewährleisten, ist, niemals eine global zugängliche Variable in einer Funktion oder Prozedur mit einem Parameter const zu modifizieren. Auf diese Weise kann sich der Angerufene auf die Referenzzahl des Aufrufers verlassen, die Semantik des Kopierens vermeiden usw. Mit anderen Worten, er teilt dem Compiler mit, dass der Wert als var effizienter übergeben wird und als var -Parameter behandelt werden kann (Das heißt, hat einen Lvalue) dann übergeben Sie es als var anstelle eines Wertes. Wenn es sich um einen verwalteten Typ handelt, z. B. eine Zeichenfolge, kann er sich auch auf die Referenz des Aufrufers verlassen, um den Speicher am Leben zu halten.

Dieser Vertrag wird durch GoToDirectory verletzt, wenn er eine globale zugängliche Zeichenfolge ändert (jeder Heapzugriff sollte in diesem Kontext als global betrachtet werden, obwohl es ein Feld eines Objekts ist). GoToDirectory sollte keinen const -Parameter haben, weil dieser gegen den von const implizierten Vertrag verstößt.

Beachten Sie, dass sich dies erheblich von dem Vertrag unterscheidet, der von const in anderen Sprachen wie C ++ impliziert wird. Es ist bedauerlich, dass es zu dieser Zeit kein besseres Wort gab. Was es wirklich sagt, ist, dass die Funktion oder Prozedur in Bezug auf Variablen rein ist, die mit dem formalen Typ des const -Arguments kompatibel sind, nicht, dass es das Argument nicht ändert. Es ist einfacher, sich daran zu erinnern, dass const nicht auf Parameter einer Funktion oder Prozedur angewendet wird, die einen Nebeneffekt hat.

Diese Faustregel kann verletzt werden, wenn der Nebeneffekt des Schreibens auf ein globales Element für die Prozedur oder Funktion oder für Prozeduren oder Funktionen, die sie aufruft, nicht sichtbar ist. Das ist in der Regel sehr schwierig, um triviale Fälle von außen zu gewährleisten (z. B. einen einfachen Eigenschaften-Setter), und sollte nur verwendet werden, wenn eine Leistungseinschränkung den Overhead der Wertkopie nicht leisten kann. Mit anderen Worten, Sie sollten besser eine Performance-Spur in der Hand haben, um es zu rechtfertigen, oder es ist für den zufälligen Beobachter offensichtlich, dass eine Kopie teuer wäre.

    
chuckj 09.01.2014 22:15
quelle
2

FWIW, ich kann das Problem nicht lokal mit XE5 Update 2, Android 4.4.2, auf einem Nexus 7 mit Ihrer Nicht-FMX-Version reproduzieren. Das Projekt wurde mithilfe Ihrer Schritt-für-Schritt-Anweisungen (Kopieren / Einfügen von Code) erstellt und im Debug-Modus auf dem Gerät ausgeführt. Die Protokollausgabe war:

Um sicher zu sein, dass ich es nicht reproduzieren konnte, habe ich die Anwendung mehrmals mit denselben Ergebnissen erstellt und ausgeführt.

Die FMX-Version weist jedoch inkonsistente Ergebnisse auf. Das erste Mal, als ich es ausgeführt und aufgebaut habe, hat es nach dem dritten ShowMessageFmt eine Zugriffsverletzung erzeugt und musste gestoppt werden. Ich habe es dann erneut erstellt, ausgeführt und konnte alle vier ShowMessageFmt -Dialoge sehen, aber das letzte zeigte einen falschen Wert:

%Vor%

Die dritte und vierte Build- und Laufwiederholung haben die gleiche Ausgabe wie die zweite erzeugt.

    
Ken White 22.12.2013 20:55
quelle
2

Ich würde sagen, das ist ein Fehler. Es ist offen und R & amp; D-Team bei Embarcadero wird es untersuchen.

    
Marco Cantù 23.12.2013 16:38
quelle

Tags und Links