这个 Embarcadero文章 讨论XE7 IDE的内存问题包含以下内容:
注意“泛型增长”
另一种可能取决于您的应用程序代码并导致编译器和调试器使用的内存增加的情况与使用通用数据类型的方式有关。 Object Pascal编译器的工作方式可以导致基于相同的通用定义生成许多不同类型,有时甚至是在不同模块中编译的完全相同的类型。虽然我们不一定会建议删除泛型,但恰恰相反,有几个选项需要考虑:
- 尽量避免定义核心泛型类型的单元的循环单元引用
- 尽可能定义和使用相同的具体类型定义
- 如果可能,重构泛型以在基类中共享代码,泛型类从中继承
我理解的最后一项。前两个我不太清楚。
这些问题是否只影响IDE性能,还是会影响编译代码的大小?
例如,考虑第二项,如果我申报 TList<Integer>
在两个单独的单元中,我将在可执行文件的每个单元中获得两个单独的代码块吗?我当然希望不是!
第2点。 这指的是在可能的情况下实例化相同的泛型类型。例如使用 TList<Integer>
在所有地方,而不是有两个泛型类型 TList<Integer>
和 TList<SmallInt>
。
声明和使用 TList<Integer>
在几个单位将只包括 单一副本 的 TList<Integer>
在exe文件中。另外,宣布 TIntegerList = TList<Integer>
会产生同样的结果。
普通臃肿的人指的是有完整的 TList<T>
复制您使用的每种特定类型,即使基础生成的代码是相同的。
例如: TList<TObject>
和 TList<TPersistent>
将包括两个单独的副本 TList<T>
即使生成的代码可以折叠成单个代码。
这让我们感动 第3点。 在公共类代码中使用基类,然后在其上使用泛型类来获得类型安全性,可以在编译期间和exe文件中节省内存。
例如,在非泛型上构建泛型类 TObjectList
只会为每种特定类型包含精简通用图层而不是完整图层 TObjectList
功能。报道为 QC 108966
TXObjectList<T: class, constructor> = class(TObjectList)
protected
function GetItem(index: Integer): T;
procedure SetItem(index: Integer; const Value: T);
public
function Add: T;
property Items[index: Integer]: T read GetItem write SetItem; default;
end;
function TXObjectList<T>.GetItem(index: Integer): T;
begin
Result := T( inherited GetItem(index));
end;
procedure TXObjectList<T>.SetItem(index: Integer; const Value: T);
begin
inherited SetItem(index, Value);
end;
function TXObjectList<T>.Add: T;
begin
Result := T.Create;
inherited Add(Result);
end;
他们在文章中讨论的代码膨胀(因为它是关于IDE中的内存不足问题)与生成的DCU和IDE中保存的所有元信息有关。每个DCU都包含所有使用的泛型。只有在编译二进制文件时,链接器才会删除重复项。
这意味着如果你有 Unit1.pas
和 Unit2.pas
两者都在使用 TList<Integer>
都 Unit1.dcu
和 Unit2.dcu
有二进制代码 TList<Integer>
汇编。
如果你宣布 TIntegerList = TList<Integer>
在 Unit3
并使用它 Unit1
和 Unit2
你可能会认为这只包括已编译的 TList<Integer>
在 Unit3.dcu
但不是在另外两个。但不幸的是情况并非如此。