Anzeigen: Object Pascal C++
Anzeigeeinstellungen

Migrieren von Object Pascal-Code zu mobilen Anwendungen vom Desktop aus

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

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

In diesem Thema wird beschrieben, wie Sie vorhandenen Object Pascal-Code für die Verwendung der mobilen Object Pascal-Compiler (für iOS-Geräte, iOS-Simulator und Android) migrieren.


Entfernen von Datentypen, die nicht von den mobilen Object Pascal-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, System.RawByteString, System.UTF8String

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 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 (Object Pascal).

AnsiString und ShortString

Diese Gruppe enthält "abgeleitete" Typen, wie UTF8String und RawByteString und ShortStrings mit durch die folgende Syntax angegebener expliziter Länge:

type TStr = string[127];

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 (Object Pascal).

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 Appmethod an anderer Stelle nicht mehr verwendet.

OpenString kann häufig durch "array of byte" ersetzt werden (siehe die Funktion ShortStringToString() im Codebeispiel ShortStringToString (Object Pascal)). "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

Verwenden von nullbasierten Strings

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

Indizierung von Strings in den Object Pascal-Compilern
Object Pascal-Compiler String-Indizierung

Mobile Object Pascal-Compiler:


  • iOS-Gerät
  • iOS-Simulator
  • Android

0-basiert

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

Object Pascal-Desktop-Compiler:


  • Win32
  • Win64
  • Mac OS X

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 Object Pascal-Compiler die Meldung W1068 Das interne Ändern von Strings wird möglicherweise künftig nicht mehr unterstützt (Object Pascal) 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:

Object Pascal-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 (Object Pascal)

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 Object Pascal-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 Object Pascal.-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 Object Pascal-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 Object Pascal-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 Object Pascal-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 Object Pascal-Compilern unterstützt:

Automatische Referenzzählung

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

Hinweis: Bei Object Pascal-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


Meine Werkzeuge
In anderen Sprachen