如果释放了单个项目,则 TryGetvalue 不起作用 - 如果不释放,则会泄漏内存



我发现我的FMX应用程序的Win32编译有一个错误!

问题很简单,如果我{$define FREE_MYPOI},TryGetValue 会返回未知的 myPOi。如果我不定义它(即在将myPOI添加到字典后不释放每个实例(,那么当应用程序退出时,Eurekalog会报告内存泄漏(我的POI显然没有被释放(。

最有趣的是,这个问题不会出现在定义了FREE_MYPOI的Android版本的代码中。(谢天谢地(

我向词典添加项目的方式是否正确?(适用于安卓和Win32 FMX(

这是核心代码:

type
TMyPOI = class(TObject)
public
Value: Integer;
Timestamp: TDateTime;
end;
...
function TForm2.CreateOrUpdate(username: String; NewTimestamp: TDateTime): String;
var
poiTimeStamp: TDateTime;
myPOI: TMyPOI;
Index: Integer;
begin
if PoiDict.TryGetValue(username, myPOI) then
begin
// existing POI
Result := InttoStr(myPOI.Value) + ' ' + DateTimeToStr(myPoi.Timestamp);
poiTimestamp := myPOI.Timestamp;
// update the Poi's timestamp
myPOI.Timestamp := NewTimeStamp;
PoiDict.AddOrSetValue(username, myPOI);
end
else
begin
// add a new POI
Index := Random(999);
Result := IntToStr(Index) + ' ' + DateTimeToStr(NewTimeStamp);
myPOI := TMyPOI.Create;
{$ifdef FREE_MYPOI}
try
{$endif}
myPOI.Value := Index;
myPOI.Timestamp := NewTimeStamp;
PoiDict.Add(username, myPOI);
{$ifdef FREE_MYPOI}
finally
myPOI.Free;
end;
{$endif}
end;
end;
initialization
PoiDict := TDictionary<string, TMyPOI>.Create;
finalization
PoiDict.Free;
end.

附录:这不是 ARC 特有的问题。这是一个关于管理对象引用的问题。

首先,永远不要Free您刚刚创建并添加到任何类型的集合中的任何对象。应将该对象的所有权转移到该集合。以下代码在 Windows 上本质上是被破坏的 - 因为你在字典中创建悬空引用 - 它们迟早会吹到你身上。

myPOI := TMyPOI.Create;
{$ifdef FREE_MYPOI}
try
{$endif}
myPOI.Value := Index;
myPOI.Timestamp := NewTimeStamp;
PoiDict.Add(username, myPOI);
{$ifdef FREE_MYPOI}
finally
myPOI.Free;
end;
{$endif}

应使用对值具有所有权的TObjectDictionary。这将在所有平台上没有泄漏。

PoiDict := TObjectDictionary<string, TMyPOI>.Create([doOwnsValues]);

接下来,myPOI是一个对象,所以你不必使用更改的时间戳再次添加它,你可以直接更改时间戳 -PoiDict.TryGetValue(username, myPOI)只会给你引用,它不会创建对象的副本。

更正后的代码如下所示:

function TForm2.CreateOrUpdate(username: String; NewTimestamp: TDateTime): String;
var
poiTimeStamp: TDateTime;
myPOI: TMyPOI;
Index: Integer;
begin
if PoiDict.TryGetValue(username, myPOI) then
begin
// existing POI
Result := InttoStr(myPOI.Value) + ' ' + DateTimeToStr(myPoi.Timestamp);
poiTimestamp := myPOI.Timestamp;
// update the Poi's timestamp - this will update object in dictionary 
myPOI.Timestamp := NewTimeStamp;
end
else
begin
// add a new POI
Index := Random(999);
Result := IntToStr(Index) + ' ' + DateTimeToStr(NewTimeStamp);
myPOI := TMyPOI.Create;
try
myPOI.Value := Index;
myPOI.Timestamp := NewTimeStamp;
PoiDict.Add(username, myPOI);
except
myPOI.Free;
raise;
end;
end;
end;
initialization
PoiDict := TObjectDictionary<string, TMyPOI>.Create([doOwnsValues]);
finalization
PoiDict.Free;
end.

您正在释放存储/添加到字典中的对象。不要释放它!或者,更好的是,使用接口而忘记手动释放对象。

P. s. 为什么会这样? - 因为对象不是按值存储的,而是按指针/引用存储的。

附言我希望你使用TObjectDictionary和值不要泄漏。


我再次阅读了您的问题,您绝对不使用TObjectDictionary或手动清理字典值,这会导致内存泄漏。您必须在字典清理/释放时释放对象,而不是在Add之后。

相关内容

最新更新