Migrieren von Delphi-Code zu mobilen Anwendungen vom Desktop aus

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Gesichtspunkte für geräteübergreifende Delphi-Anwendungen


In diesem Thema wird beschrieben, wie Sie vorhandenen Delphi-Code für die Verwendung der mobilen Delphi-Compiler migrieren:

Entfernen von Datentypen, die nicht von den mobilen Delphi-Compilern unterstützt werden

Code, in dem einer der folgenden nicht unterstützten Typen verwendet wird, sollte entweder entfernt oder mit einem alternativen Typ umgeschrieben werden:

WideString, AnsiString, ShortString, AnsiChar, PAnsiChar, PWideChar, Openstring

In Desktop-Anwendungen verwendeter Typ
(1-basiert)

In mobilen Apps verwendeter Typ
(0-basiert)

System.WideString

System.String

System.AnsiString, System.ShortString

Verwendung entfernen.

Verwenden Sie ggf. "array of byte".

System.AnsiChar

System.Char, System.Byte, System.UInt8

System.PAnsiChar, System.PWideChar

System.SysUtils.TStringBuilder, System.String, System.MarshaledString

System.Openstring

Verwenden Sie ggf. "array of byte".

Im Folgenden finden Sie Einzelheiten zum Ersetzen dieser nicht unterstützten Typen.

WideString

In manchen Fällen kann WideString durch String, und TWideStringDynArray durch TStringDynArray ersetzt werden.

Wenn Sie aus bestimmten Gründen WideString auf der mobilen Plattform verwenden müssen, sollten Sie ein Marshalling durchführen (4-Byte-Länge, Unicode-Zeichenfolge und die beiden Nullzeichen des String-Begrenzers verarbeiten). Ein Beispiel für die Verwendung von ShortString finden Sie im Codebeispiel ShortStringToString (Delphi).

AnsiString und ShortString

Diese Gruppe enthält AnsiString und die explizite Länge ShortStrings durch die Syntax : type TStr = string[127];.

Hinweis: Die "abgeleiteten" Typen UTF8String und RawByteString werden auf mobilen Geräten unterstützt.

Entfernen Sie Vorkommen von AnsiString und ShortString, oder ändern Sie sie je nach ursprünglicher Verwendung. In manchen Fällen ist ein "array of byte" (wie z. B. System.Types.TByteDynArray) ausreichend.

Manchmal müssen Sie bei Bedarf das ältere Format decodieren und erneut codieren. Die meisten Vorkommen von AnsiString können direkt durch den Standardtyp String ersetzt werden. Die meisten extern für diesen Typ gespeicherten Informationen verwenden Streams oder Klassen, die Streams verwenden, wie TStringList oder ähnliche Klassen. Diese Klassentypen unterstützen BOMs (Byte Order Marks) und werden bei Bedarf automatisch decodiert. Wenn Umwandlungen erforderlich sind, verwenden Sie die Klasse TEncoding, um die Bytes direkt abzurufen. TStrings, die Basisklasse von TStringList, unterstützt die explizite Spezifikation von TEncoding-Instanzen in Methoden, wie TStrings.SaveToFile und TStrings.LoadFromFile, sodass Ihr Programm den normalen String-Typ ohne Berücksichtigung der endgültig erforderlichen Codierung für die Speicherung außerhalb des Programms verwenden kann.

Verwenden Sie für Code mit ShortStrings TEncoding zur Verwaltung der Unterschiede zwischen der im aktuellen String-Typ verwendeten UTF16-Zeichenrepräsentation und der 8-Bit-ANSI-Repräsentation des alten ShortStrings.

Siehe das Codebeispiel ShortStringToString (Delphi).

AnsiChar

Verwenden Sie (Wide)Char oder Byte(UInt8) anstelle von AnsiChar.

Je nach ursprünglicher Semantik:

  • Wenn die ursprüngliche Semantik "Zeichen" ist, verwenden Sie Char mit UNICODE-Konvertierung.
  • Wenn die ursprüngliche Semantik die 8-Bit-Speicherung ist, verwenden Sie den Typ Byte.

PAnsiChar und PWideChar

Wenn diese Typen auf Ansi/Unicode-Strings zeigen, verwenden Sie String- oder TStringBuilder-Objekte anstatt des Typs PAnsiChar/PWideChar.

Wenn die ursprüngliche Semantik sich auf einen API-Aufruf bezieht, ersetzen Sie ihn durch die API-Marshalling-Funktionalität. In der Regel genügt System.MarshaledString.

Openstring

System.Openstring ist ein altes Sprachelement. Derzeit verwendet System.Generics.Defaults den Typ OpenString, wird jedoch in RAD Studio an anderer Stelle nicht mehr verwendet.

OpenString kann häufig durch "array of byte" ersetzt werden (siehe die Funktion ShortStringToString() im Codebeispiel ShortStringToString (Delphi)). "Array of byte" erstellt hier einen offenen Array-Parameter und akzeptiert "arrays of byte" von beliebiger Länge, genau wie OpenString Strings jeder beliebigen Größe zuließ.

Siehe http://www.drbob42.com/uk-bug/hood-03.htm (EN)

Verwenden von nullbasierten Strings

Für die mobilen Delphi-Compiler haben Strings eine 0-basierte Indizierung. Darüber hinaus werden sie wahrscheinlich zukünftig unveränderlich (konstant) sein.

Indizierung von Strings in den Delphi-Compilern
Delphi-Compiler String-Indizierung

Mobile Delphi-Compiler:


0-basiert

(der Startindex des ersten Zeichens in einem String ist null)

Delphi-Desktop-Compiler:


1-basiert

(der Startindex des ersten Zeichens in einem String ist eins)

Sie sollten Code neu schreiben, in dem von 1-basierten oder veränderbaren Strings ausgegangen wird.

  • Nullbasierte Strings: Schreiben Sie den Code für alle 1-basierten Indizes für den Zugriff auf Zeichenelemente eines Strings so um, dass eine 0-basierte Indizierung verwendet wird (Beispiel folgt).
  • Unveränderbare Strings: Wenn Sie ein Zeichen in einem unveränderbaren String ändern möchten, müssen Sie den String in zwei oder mehrere Strings unterteilen, und dann diese Teile wieder kombinieren, oder ein TStringBuilder-Objekt verwenden.
    Die folgende übliche Operation (Indizierung eines Strings und Änderung des Strings) kann beispielsweise nicht mit unveränderbaren Strings ausgeführt werden:
    S[1] := 'A';
Wenn Sie eine String-Operation wie diese verwenden, geben die mobilen Delphi-Compiler die Meldung W1068 Das interne Ändern von Strings wird möglicherweise künftig nicht mehr unterstützt (Delphi) aus. An einem gewissen Punkt muss diese Warnung durch eine Fehlermeldung ersetzt werden; Sie können die Warnung sofort auf der Seite Hinweise und Warnungen der Projektoptionen in eine Fehlermeldung konvertieren.

Verwenden Sie TStringHelper zur Behandlung von Strings in mobilen und Desktop-Apps

Die Klasse oder der unterstützende Record System.SysUtils.TStringHelper ist bei der Arbeit mit Strings und dem Schreiben von plattformunabhängigem Code äußerst nützlich. TStringHelper kann in allen Umgebungen (Desktop und mobil) eingesetzt werden. TStringHelper führt automatische Umwandlungen durch, daher können Sie TStringHelper für 0-basierte und 1-basierte Strings verwenden. Intern sind alle Funktionen und Eigenschaften von TStringHelper in allen Szenarien nullbasiert.

Einige der RTL-Funktionen, die mit 1-basierten Strings arbeiten, haben direkte Gegenstücke in TStringHelper-Funktionen, wie in der folgenden Tabelle gezeigt:

Delphi-RTL-Funktion
(1-basiert)

TStringHelper-Funktion
(0-basiert)*

System.Pos
TStringHelper.IndexOf
System.Delete
TStringHelper.Remove
System.Copy
TStringHelper.Substring
System.SysUtils.Trim
TStringHelper.Trim
* Diese Hilfsfunktionen arbeiten sowohl mit 1-basierten als auch mit 0-basierten Strings korrekt.

Dieses Thema enthält Beispiele aller oben vorgeschlagenen Ersetzungen (mit Ausnahme von Delete-Remove).
In den folgenden Unterthemen werden die Änderungen beschrieben, die zum Migrieren von Code von der 1-basierten zur 0-basierten String-Indizierung erforderlich sind:

Testen von unveränderbaren Strings

Führen Sie eine der folgenden Aktionen aus, um unveränderbare Strings zu testen:

  • Legen Sie die Compiler-Direktive {$WARN IMMUTABLE_STRINGS <ON|ERROR>} fest.
  • Setzen Sie auf der Seite Hinweise und Warnungen die Warnung "Das interne Ändern von Strings wird...." auf "true" oder "Fehler".

Wenn Strings im jeweiligen String selbst bearbeitet werden, wird die folgende Warnung/Fehlermeldung angezeigt: W1068 Das interne Ändern von Strings wird möglicherweise künftig nicht mehr unterstützt (Delphi)

Beispiel für die Konvertierung 1-basierter Strings in 0-basierte Strings

Das folgende Beispiel zeigt, wie ein 1-basierter String geändert wird, damit er auf allen Plattformen verwendet werden kann:

 function Trim(const S: string): string;
 var
   I, L: Integer;
 begin
   L := Length(S);
   I := 1;
   if (L > 0) and (S[I] > ' ') and (S[L] > ' ') then Exit(S);
   while (I <= L) and (S[I] <= ' ') do Inc(I);
   if I > L then Exit('');
   while S[L] <= ' ' do Dec(L);
   Result := Copy(S, I, L - I + 1);
 end;

Zugreifen auf Zeichen in einem String mit TStringHelper.Chars

Chars ist eine nützliche Eigenschaft von TStringHelper:

Chars[Index]

Diese schreibgeschützte Eigenschaft kann auf alle Zeichen eines Strings zugreifen. Denken Sie daran, dass Strings für die mobilen Delphi-Compiler immer nullbasiert sind.

Beispiel für die Verwendung der Eigenschaft Chars für den Zugriff auf einzelne Zeichen:

 function Trim(const S: string): string;
 var
   I, L: Integer;
 begin
   L := S.Length - 1;
   I := 0;
   if (L > -1) and (S.Chars[I] > ' ') and (S.Chars[L] > ' ') then Exit(S);
   while (I <= L) and (S.Chars[I] <= ' ') do Inc(I);
   if I > L then Exit('');
   while S.Chars[L] <= ' ' do Dec(L);
   Result := S.SubString(I, L - I + 1);
 end;

Zugreifen auf den ersten und den letzten Index in einem String mit System.Low und System.High

Sie können die intrinsischen Delphi.-Routinen High und Low für Strings verwenden.

  • Low(s) gibt für einen 0-basierten String 0, aber für einen 1-basierten String 1 zurück.
  • High(s) gibt für einen 0-basierten String Length(s) - 1, aber für einen 1-basierten String Length(s) zurück.

Mit dem folgenden Befehl können Sie den ersten Index eines Strings ermitteln:

Low(string)

Beispielsweise können Sie diese häufig verwendete for-Anweisung:

for I := 1 to Length(S) do

durch diese for-Anweisung ersetzen:

for I := Low(S) to High(S) do

Ein weiteres Beispiel: Wenn s = '' (leer):

  • Low(s) = 0 und High(s) = -1 für 0-basierte Strings.
  • Low(s) = -1 und High(s) = 0 für 1-basierte Strings.

Ersetzen der System.Pos-Funktion durch TStringHelper.IndexOf

Die Funktion System.Pos kann mit 1-basierten Strings, nicht jedoch mit 0-basierten Strings verwendet werden. Statt Pos können Sie TStringHelper.IndexOf verwenden. Die Funktion IndexOf gibt die nullbasierte Indexposition des Parameters Value (Char oder String) zurück, wenn der String gefunden wird, oder -1, wenn er nicht gefunden wird.


Beispiel:

 s := 'The quick brown fox jumps over the lazy dog'; // s is a string type variable.
 WriteLn(Pos('ow', s));    // 13
 WriteLn(s.IndexOf('ow')); // 12

Hinweis: Die Funktion TStringHelper.IndexOf entspricht der .NET-Implementierung mit folgender Ausnahme: wenn der Value-String leer ist, gibt .NET 0 zurück, die Delphi-RTL aber -1.

Ersetzen der System.Copy-Funktion durch TStringHelper.Substring

Die Funktion System.Copy kann mit 1-basierten Strings, nicht jedoch mit 0-basierten Strings verwendet werden. Statt Copy können Sie TStringHelper.Substring verwenden:

  function TStringHelper.Substring(StartIndex: Integer; Length: Integer): string;

Die Funktion Substring gibt einen String zurück, der in dieser Instanz dem Teilstring mit der angegebenen Länge ab StartIndex entspricht. Wenn StartIndex größer oder gleich der Länge dieser Instanz ist, gibt Substring einen leeren String zurück. Wenn Length null oder negativ ist, gibt Substring einen leeren String zurück.

Beispiel

 s := '123456789'; // s is string type variable.
 writeln( Copy(s, 2, 3) );     // 234
 writeln( s.Substring(1, 3) ); // 234

Hinweis: Die Funktion TStringHelper.Substring entspricht der .NET-Implementierung mit folgender Ausnahme: .NET löst eine ArgumentOutOfRangeException-Exception aus, wenn StartIndex plus Length eine Position außerhalb dieser Instanz angeben, oder wenn StartIndex oder Length kleiner als null ist. Die Delphi-RTL löst jedoch keine Exception aus. Für die obige Bedingung gibt Substring einen leeren String zurück.

Aktualisieren der Array-Typen

Aktualisieren Sie alle Array-Deklarationen, damit sie dynamisch sind. Verwenden Sie eine der folgenden Formen:

var x: array of Integer;
x: TArray<Integer>;

Manchmal muss eine Struktur (Record) mit einer externen Funktion übergeben werden, und die Struktur enthält ein in einer bestimmten Länge deklariertes Array. Dies gilt insbesondere für Zeichen-Arrays, die in der Struktur selbst deklariert sind. In diesem Fall und nur in diesem Fall ist Folgendes zulässig:

type
   rec = record
    Flags: Integer;
    Chars: array[MAX_PATH] of Char;
  end;

Wenn eine externe Funktion ein Array direkt übernimmt, verwenden Sie stattdessen ein dynamisches Array. Für UTF8-Zeichen-Arrays:

Verwenden eines Funktionsaufrufs in einem "try-except"-Block zur Vermeidung von nicht abgefangenen Hardware-Exceptions

Bei Compilern für iOS-Geräte können except-Blöcke nur eine Hardware-Exception abfangen, wenn der try-Block einen Methoden- oder Funktionsaufruf enthält. Dieser Unterschied steht im Zusammenhang mit dem LLVM-Backend des Compilers, das nicht zurückkehren kann, wenn keine Methode/Funktion im try-Block aufgerufen wird.

Folgendes Beispiel zeigt, wie ein try-except-Block aufgebaut sein muss, damit er eine Hardware-Exception abfangen kann:

var
  P: ^Integer = nil;

procedure G1;
begin
  P^ := 42;
end;

begin
  try
    G1;
  except
    writeln('Catch:G1 - pass');
  end;
end.

Selbst wenn ein Quellcodeblock so aussieht, als ob er einen Funktionsaufruf enthält, könnte das nicht der Fall sein, wenn die Funktion inline ist. Zu der Zeit, zu der LLVM die Maschinenanweisungen erzeugt, ist der Inlining-Prozess bereits aufgetreten und in dem try-Block befindet sich kein Funktionsaufruf mehr.

Verwenden von intrinsischen unteilbaren (atomic) Funktionen anstelle der Assembly-Sprache

Der integrierte Assembler wird von den mobilen Delphi-Compilern nicht unterstützt. Wenn Sie Speichervariablen funktional unteilbar (atomic) austauschen, vergleichen und austauschen, inkrementieren und dekrementieren müssen, können Sie die neuen intrinsischen "Atomic"-Funktionen verwenden.

Mit unteilbaren (atomic) Operationen werden primitive, blockierende Multithreaded-Klassen implementiert, und diese Operationen stellen die primitiven Klassen bereit, die für die Implementierung von sogenannten "nicht-blockierenden" Strukturen erforderlich sind. Diese Art von Operationen werden als Standardfunktionen oder "intrinsische" Funktionen implementiert.

In plattformübergreifenden Anwendungen können intrinsische unteilbare (atomic) Funktionen innerhalb von {$IFDEF} für AUTOREFCOUNT oder NEXTGEN bedingt verwendet werden.

Intrinsische unteilbare (atomic) Funktionen

Die folgenden intrinsischen unteilbaren (atomic) Funktionen werden von den mobilen Delphi-Compilern unterstützt:

Automatische Referenzzählung

Die mobilen Delphi-Compiler (DCCIOS32, DCCIOS32ARM und DCCAARM) verwenden die automatische Referenzzählung (ARC, Automatic Reference Counting) für Klassen. Dies unterscheidet sich von dem Schema, das von den Delphi-Desktop-Compilern (DCC32, DCC64 und DCCOSX)verwendet wird. Alle Delphi-Compiler unterstützen jedoch ARC für Interfaces, Strings und dynamische Arrays, daher erweitern die mobilen Delphi-Compiler ARC eigentlich lediglich für Klassen. ARC umfasst die automatische Speicherverwaltung und -freigabe.

Hinweis: Bei Delphi-Compilern, die ARC unterstützen, können Objektinstanzen, die aufeinander verweisen, Arbeitsspeicher sperren, ohne dass eine dieser Referenzen als schwache Referenz gekennzeichnet wird.

Weitere Informationen zu ARC und schwachen Referenzen finden Sie unter:

Siehe auch