delphi - DynArraySize() 只能在最多 649 个整数元素的数组中正常工作
问题描述
我在Delphi 10.2 Update 2中遇到了与 RTTI 相关的问题,并且能够将其追踪到更少的代码(见下文)。
我有一些发布类型属性的TPersistent
后代类。当我通过它接收它的值并通过它查询它的大小时,它的大小只能达到 649 个元素的大小。在这个特殊计数之上,返回一些非常大的尺寸值。TMyObj
TArray<Integer>
GetDynArrayProp()
DynArraySize()
请注意,我的数组是从TDictionary<Integer,Boolean>
'Keys
属性的实例生成的,它有自己的ToArray
方法。我还尝试进行修改TMyObj.GetDynArray
,以便它TArray<Integer>
直接返回一个实例并且它工作正常。因此,我认为这可能以某种神秘的方式相互关联。
我的使用有什么问题DynArraySize()
?动态数组这种神秘行为的背后是什么?
program RTTIPropDynArray;
{$APPTYPE CONSOLE}
uses
System.Classes, System.Generics.Collections, System.SysUtils, System.TypInfo;
type
TMyDict = TDictionary<Integer,Boolean>;
TMyArray = TArray<Integer>;
TMyObj = class(TPersistent)
private
FValues: TMyDict;
function GetDynArray: TMyArray;
public
constructor Create(const ACount: Integer);
destructor Destroy; override;
published
property DynArray: TMyArray read GetDynArray;
end;
{ TMyObj }
constructor TMyObj.Create(const ACount: Integer);
begin
FValues := TMyDict.Create;
while FValues.Count < ACount do
FValues.AddOrSetValue(Random(MaxInt), False);
end;
destructor TMyObj.Destroy;
begin
FreeAndNil(FValues);
inherited;
end;
function TMyObj.GetDynArray: TMyArray;
begin
Result := FValues.Keys.ToArray;
end;
function Test(const ACount: Integer): Boolean;
var
LInstance: TMyObj;
LExpectedSize: Integer;
LDynArraySize: Integer;
begin
LInstance := TMyObj.Create(ACount);
try
LExpectedSize := Length(LInstance.DynArray);
LDynArraySize := DynArraySize(GetDynArrayProp(LInstance, 'DynArray'));
Result := LExpectedSize = LDynArraySize;
if not Result then
WriteLn(Format('Expected size: %d; DynArraySize: %d', [LExpectedSize, LDynArraySize]));
finally
LInstance.Free;
end;
end;
var
LCount: Integer;
begin
Randomize;
LCount := 1;
while Test(LCount) do
Inc(LCount);
ReadLn;
end.
解决方案
简短回答:您的代码已损坏
长答案:
对 getter 的调用正在创建一个新数组(参见参考资料),该数组TEnumerable<T>.ToArrayImpl
在(在此处放置一个断点并查看反汇编程序 - 它显示)System.Generics.Collections.pas
的结尾处被释放。由于没有其他对该数组的引用,因此它的内存被释放(如果您进一步深入,您会看到它最终以. 这意味着对该函数的每次调用都会返回一个悬空指针!System.TypInfo.GetDynArrayProp
@DynArrayClear
System.pas
_FreeMem
现在为什么你在之前的所有调用中都得到正确的结果?巧合 - 内存没有被其他任何东西重新分配。
想到了两种不涉及重写 getter 的可能解决方案:
System.Rtti.pas
使用来自as的 RTTITValue
使参考保持活动状态- 编写您自己的版本以
GetDynArrayProp
使引用保持活动状态-但您必须确保在之后始终调用 DynArrayClear 否则会造成内存泄漏
我个人会使用第一个。
推荐阅读
- amazon-web-services - 如何将默认 VPC 添加到过去经典 EC2 实例中的区域?
- php - 扩展 DateTime 类
- webpack - Webpack - 如何在另一个 javascript 文件中要求一个条目包
- c# - 如何使用从返回数据表的方法返回的值?
- ruby-on-rails - ActiveStorage - Safari 缓存附件
- php - 允许根据设置的权限以及模型在 Laravel 中的属性值编辑模型
- selenium - ElementClickInterceptedException Selenium 无法单击按钮 Python
- python - QGroupBox 按钮未显示
- java - 如何使用 Criteria Builder Java 从另一个类属性内的类属性中选择一个元素?
- javascript - 平台设置后无法启用 prod 模式