移植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移动编译器:
  
   DCCIOSARM
  
   DCCIOS32
  
   DCCAARM
  
  
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包含内存的自动管理和处置。 
:  对于支持ARC Delphi 编译器,  相互参照的对象实例可以有效的锁定内存,无须其中一个引用设置为弱引用属性。
更多信息请看ARC和弱引用: 

原文链接


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


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值