转自: http://www.52jike.com/thread-34-1-1.html
本文
讲述的是如何移植现有的
Delphi桌面
代码,使之可以用
Delphi
移动编译器编译。
去除Delphi移动编译器不支持的数据类型
使用下列不支持类型的代码要么删掉要么用替代类型重写:
WideString,AnsiString, ShortString, AnsiChar, PAnsiChar, PWideChar, Openstring
以下是不支持类型的替换细节:
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 .
根据原来的语义:
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开始索引的。另外,它们很可能在未来变成不可变的(常量)。
我们建议你重写那些假定字符串从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函数, 如下表所述:
* helper函数能同时用于从1和从0开始的字符串。
这个题目包含了所有上面建议的替换的例程(除了 Delete-Remove).
下面这些子题目说明了把你的代码从1索引字符串移植到0索引所需的改变
下面这些子题目说明了把你的代码从1索引字符串移植到0索引所需的改变
- #Replacing the System.Pos Function with TStringHelper.IndexOf
- #Replacing the System.Copy Function with TStringHelper.Substring
- #Example of Converting Strings from 1-based to 0-based
测试不可变字符串
为了测试不可变字符串, 执行下面之一:
- 设置编译器指令 {$WARN IMMUTABLE_STRINGS <ON|ERROR>}.
- 在 Hints and Warnings 页, 把 "Modifying strings in-place...." 设置为 "true" 或 "error".
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.Low和System.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字符数组:
- 为了从UTF8转换过来, 使用TBytes 还有 System.SysUtils.TEncoding.GetString(Bytes).
- 为了转换到UTF8, 使用TBytes 还有 System.SysUtils.TEncoding.GetBytes(S).
在一个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和弱引用:
原文链接