移植Delphi桌面代码到移动平台

转自: http://www.52jike.com/thread-34-1-1.html

本文讲述的是如何移植现有的Delphi桌面代码,使之可以用Delphi移动编译器编译。


去除
Delphi移动编译器不支持的数据类型


  使用下列不支持类型的代码要么删掉要么用替代类型重写:
WideString,AnsiString, ShortString, AnsiChar, PAnsiChar, PWideChar, Openstring
  
桌面应用使用的类型
  (1-based)
  
  
移动应用使用的类型
  (0-based)
  
           
        
消除使用.
  
考虑使用 'array of byte'
  
           
           
        
考虑使用'array of byte'
  
以下是不支持类型的替换细节:

WideString某些情况下, WideString 可以用String替换. 
如果你出于某些原因要在移动平台上使用WideString,你要把它整改成可处理4字节长度,处理Unicode字符序列,两个null字符表示字符串结束。例如使用ShortString的代码例子 ShortStringToString (Delphi)


AnsiString和 ShortString 
这一组包含有一些“衍生”类型比如UTF8String 和 RawByteString, 以及使用下面语法定义的长度确定的ShortStrings
type TStr = string[127];是删除还是修改AnsiString 和 ShortString的实例, 取决于原来的用法. 在一些情况下, 'array of byte' {比如 System.Types.TByteDynArray) 已足够. 
在很多时候,你需根据需要编码和解码旧的格式。大多数AnsiString的使用可以直接用缺省String类型替换。大部分以这个类型保存在外部的信息使用流或使用流的类,例如TStringList或相似的类。这些类类型支持字节顺序标记(BOM),能在需要时自动解码。在有必要转换的地方,使用TEncoding类来直接获取字节数据。实际上,TStrings,,TstringList的基类,支持诸如TStrings.SaveToFile 和 TStrings.LoadFromFile等方法里TEncoding 实例的显式说明,因此你的程序可以使用普通的String类型,而不用管程序外部存储所需的最后编码是什么。 
对于使用short strings的代码, 用TEncoding来处理最新字符串类型使用的UTF16表示和旧的 short string使用的8位ANSI表示之间的差异。请看代码例子ShortStringToString (Delphi)


AnsiChar
用 (Wide)Char 或 Byte(UInt8) 来替换 AnsiChar

根据原来的语义: 

  • 如果原来的语义是'Character', 使用UNICODE转换的Char.
  • 如果原来的语义是8位存储使用Byte 类型.

PAnsiChar 和 PWideChar 

如果这些类型指向Ansi/Unicodestring, 使用 String或 TStringBuilder 对象代替PAnsiChar/PwideChar类型. 
如果原来语义和API调用有关, 把它替换成API marshaling功能. 一般 System.MarshaledString 足够了. 


Openstring
System.Openstring 是一个老旧的语言元素。目前System.Generics.Defaults 使用OpenString类型,但它很少出现在除了RAD Studio的其它地方. 
通常"array of byte"可以用来代替OpenString,就像在这个例子代码的ShortStringToString()函数里面可以看到ShortStringToString (Delphi). "Array of byte" 在这里用来组成一个开放数组参数,可以接受任意长度字节数组,就像允许定义任意长度字符串的OpenString。 
请看 http://www.drbob42.com/uk-bug/hood-03.htm 

使用从0开始的Strings
对于Delphi移动编译器,字符串是从0开始索引的。另外,它们很可能在未来变成不可变的(常量)。

  
Delphi编译器的字符串索引
  

  
Delphi 编译器
  
  
字符串索引
  
  
Delphi移动编译器:
           
  
0-based 
  
  (字符串的第一个字符的索引是0)
  
  
Delphi桌面编译器:
  
  DCC32
  
  DCC64
  
  DCCOSX
  
  
1-based 
  
  (字符串的第一个字符的索引是1)
  
       
我们建议你重写那些假定字符串从1开始索引或可变的代码.

  • Zero-Based Strings: 对于使用从1开始索引来访问字符串字符元素的,重写代码来使用0开始索引(下面有个例子)。     
  • Immutable Strings: 如果你想要修改一个不可变字符串的中间,你只能把它分成两或多个分段,然后把各个分段连接起来,或者使用TStringBuilder
例如, 下面的一般操作 (索引操作与修改字符串) 不能用于不可变字符串:
S[1] := 'A';
如果你使用了这样的操作,Delphi移动编译器会弹出警告W1068 Modifying strings in place may not be supported inthe future (Delphi)。在某种意义来说,这个警告应该用错误代替;可以立刻在项目选项的Hints and Warnings 页把它转变成错误。


我们建议使用TstringHelper来处理桌面与移动应用中的字符串

这个类和记录helper System.SysUtils.TStringHelper 对于处理字符串和编写平台独立代码很有用。你可以在所有环境使用TStringHelper (桌面和移动). TstringHelper完成了自动转换, 因此你可以用TstringHelper处理从0和从1开始的字符串。TstringHelper内部的所有函数和属性在所有情况都是从0开始的。
一些用于从1开始字符串的RTL函数可以直接替换成TstringHelper函数, 如下表所述:
  
Delphi  RTL 函数
  (1-based)
  
  
TStringHelper  函数
  (0-based)*
  
  System.Pos 
  
  TStringHelper.IndexOf
  
  System.Delete 
  
  TStringHelper.Remove
  
  System.Copy 
  
  TStringHelper.Substring
  
  System.SysUtils.Trim 
  
  TStringHelper.Trim 
  
* helper函数能同时用于从1和从0开始的字符串。


这个题目包含了所有上面建议的替换的例程(除了 Delete-Remove). 
下面这些子题目说明了把你的代码从1索引字符串移植到0索引所需的改变

    测试不可变字符串
    为了测试不可变字符串, 执行下面之一: 

  • 设置编译器指令 {$WARN IMMUTABLE_STRINGS     <ON|ERROR>}.
  •  Hints and Warnings ,      "Modifying strings     in-place...." 设置为 "true"      "error".
   如果字符串的某些位置被修改,就会显示警告/错误信息: W1068 Modifying strings in place may notbe supported in the future (Delphi)

1-based字符串转换到0-based 的例子
这时一个演示如何修改1-based字符串的例子,使用所有平台。
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;

使用TStringHelper.Chars来访问一个String中的字符
Chars 是TStringHelper的一个很有用的属性: 
Chars[Index]这个只读的属性可以访问字符串的所有字符。记住Delphi移动编译器的字符串数组都是从0开始的。
使用Chars 属性来访问单个字符的例子: 
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;

使用 System.LowSystem.High来访问String的第一个和最后一个索引
你可以将Delphi内置例程High 和 Low 应用于字符串数组。

  • Low(s) 在我们的0-based字符串场景返回0, 1-based字符串返回1..
  • High(s) 在我们的0-based字符串场景返回Length(s) – 1对于1-based字符串返回Length(s) 
要检测字符串的第一位索引,使用:
Low(string)例如, 你可以替换这个常用的for语句:
for I := 1 to Length(S) do为这个for语句: 
for I := Low(S) to High(S) do另外一个例子是, 当S为空时: 
·        Low(s) = 0 和 High(s) = -1 ,对于0-based 字符串数组.
·        Low(s) = -1 和 High(s) = 0 ,对于1-based字符串数组.


用TStringHelper.IndexOf替换System.Pos函数

System.Pos 函数能用于1-based 字符串数组,不能用于0-based 字符串数组。可以用TStringHelper.IndexOf 代替Pos, 你可以用IndexOf 函数返回数值参数(一个字符或字符串)的从0开始索引的位置如果字符串能找到的话,或者如果找不到时返回-1。

例子: 
s := 'The quick brown fox jumps over the lazy dog'; // s 是string 类型的变量 
WriteLn(Pos('ow', s));    // 13 
WriteLn(s.IndexOf('ow')); // 12
: TStringHelper.IndexOf 函数与.NET实现类似,除了当字符串值为空时,.NET 返回 0, 但是 Delphi RTL 返回 -1.

用TStringHelper.Substring替换System.Copy函数
System.Copy 函数能工作于1-based 字符串数组但是不能工作于 0-based 字符串数组. 你可以用TStringHelper.Substring来替换Copy: 
  function TStringHelper.Substring(StartIndex: Integer; Length: Integer): string;

Substring 函数返回一个等同于这个实例串中从StartIndex 开始长度length的子字符串。 如果StartIndex 大于等于实例的长度,Substring 返回一个空的字符串。如果Length 为0或负值, Substring 返回空字符串。 
例子
s := '123456789'; // s is string type variable. 
writeln( Copy(s, 2, 3) );     // 234 
writeln( s.Substring(1, 3) ); // 234
: TStringHelper.Substring函数和.NET 实现类似除了.NET 会引发一个ArgumentOutOfRangeException 异常---StartIndex 加上Length 指示的位置不在样例的范围内或者如果StartIndex Length 小于0时。 但另一方面,DelphiRTL却不会引发异常对于上面情况,Substring 返回空字符串.


更新数组类型

把所有数组定义更新为动态的。使用下面其中一种:
var x: array of Integer;
x: TArray<Integer>;
有一些案例是当必须传递一个结构(记录)到外部函数时,这个结构含有一个指定长度的数组。特别是结构内已经定义好的字符数组. 在这种情况,也只有这种情况下,下面写法是允许的:
type
   rec = record
    Flags: Integer;
    Chars: array[MAX_PATH] of Char;
  end;
对于外部函数直接带数组的,换用动态数组。对于UTF8字符数组:

在一个try-except 代码块中使用函数来防止捕捉不到硬件异常 


对于DCCIOSARM 编译器, 异常块只有在try代码块包含有方法或函数调用的时候才能够捕捉硬件异常。这是一个和编译器LLVM 后端有关联的差异,如果try块里面没有方法/函数调用LLVM后端不能返回。 
这是一个怎么构造可以捕捉硬件异常的try-except 块的例子:
var  
P: ^Integer = nil; 
procedure G1;
begin  
    P^ := 42;
end; 

begin  
    try    
        G1;  
    except    
         writeln('Catch:G1 - pass');  
    end;
end.
尽管有时候一个代码块看起来像是包含有函数调用,但实际上不是的,如果那个函数是内联的。到LLVM 发生机器指令那时, 内联过程早已经展开,因此try块里面已经没了函数调用。 

使用原子内建代替汇编语言 

Delphi 移动编译器不支持内建汇编。如果你需要原子性的交换, 比较-和-交换, 加,减内存数值, 你可以使用新的内置原子函数。 
原子操作是用来实现多线程锁定的原语,为实现所谓“lock-free”架构所提供的原语。这种所需要的操作是用标准函数或内建函数实现的。 
在一个多平台应用内部,原子内建可用于{$IFDEF} 内的AUTOREFCOUNT 或 NEXTGEN 条件语句
原子内建函数 以下是Delphi移动编译器支持的原子内建函数:

自动引用计数 


Delphi移动编译器 (DCCIOS32, DCCIOS32ARM, 和 DCCAARM) 在类上使用自动引用计数(ARC),一种和Delphi桌面编译器(DCC32, DCC64, 和DCCOSX)不同的自动引用计数方案。不管怎样,所有Delphi编译器都已在接口,字符串,动态数组上支持ARC,所以实际上Delphi移动编译器仅仅是将ARC扩展到了类。ARC包含内存的自动管理和处置。 
: 对于支持ARCDelphi编译器, 相互参照的对象实例可以有效的锁定内存,无须其中一个引用设置为弱引用属性。
更多信息请看ARC和弱引用: 

原文链接


www.52jike.com  ruanzhuo 翻译整理,转载请注明出处。


©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值