delphi - Delphi - TObjectDictionary 似乎没有释放拥有的价值
问题描述
当我使用 doOwnsValues 创建 TObjectDictionary 并将 TStringList 存储为值时,在释放 TObjectDictionary 之后,我仍然可以访问 TStringList。
我修改了此处给出的示例: 使用 Generics.Collections.TObjectDictionary的示例
{$APPTYPE CONSOLE}
{$R *.res}
uses
Generics.Collections,
Classes,
System.SysUtils;
Var
MyDict : TObjectDictionary<String, TStringList>;
Sl : TStringList;
begin
ReportMemoryLeaksOnShutdown:=True;
try
//here i'm creating a TObjectDictionary with the Ownership of the
Values
//because in this case the values are TStringList
MyDict := TObjectDictionary<String, TStringList>.Create([doOwnsValues]);
try
//create an instance of the object to add
Sl:=TStringList.Create;
//fill some foo data
Sl.Add('Foo 1');
Sl.Add('Foo 2');
Sl.Add('Foo 3');
//Add to dictionary
MyDict.Add('1',Sl);
//add another stringlist on the fly
MyDict.Add('2',TStringList.Create);
//get an instance to the created TStringList
//and fill some data
MyDict.Items['2'].Add('Line 1');
MyDict.Items['2'].Add('Line 2');
MyDict.Items['2'].Add('Line 3');
//finally show the stored data
Writeln(MyDict.Items['1'].Text);
Writeln(MyDict.Items['2'].Text);
finally
//only must free the dictionary and don't need to worry for free the
TStringList assignated to the dictionary
MyDict.Free;
end;
Sl.Add('Foo added?');
Writeln(Sl[0]);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
我希望 S1 不应该被访问。是内存泄漏吗?还是错误?
解决方案
字符串列表对象Sl
已被字典释放。没有内存泄漏。Delphi 的 RTL(字典类)在这里没有错误。
因此,当您Sl.Add('Foo added?');
访问一个已被释放的对象时,这是您的代码中的一个错误。你不应该那样做。
仅仅因为您这次没有得到任何明显的错误症状并不意味着它不是错误。
一个更简单的例子:
type
TTest = class
Value: Integer;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Test: TTest;
begin
Test := TTest.Create;
try
Test.Value := 123;
finally
Test.Free;
end;
ShowMessage(Test.Value.ToString); // Bug!! Never do this!
end;
这是一个错误。但无论如何,您可能会收到一个123
消息框。
当您释放一个对象时,它的内存区域被声明为可用,以便将来可以将其他数据存储在那里。因此,在您的对象被销毁后,它的字节很可能在您离开它们的地方完好无损。但最终它们将被新数据覆盖,并且随着时间的推移,所有字节都可能发生变化。你永远不知道,也不应该知道:你不再拥有计算机内存的那部分。
当然,销毁一个对象可能不仅仅是放弃它在内存中的位置:操作系统句柄可能会被关闭,临时文件可能会被删除等等。因此,如果你使用一个释放的对象,有很多事情可能会出错,甚至如果您在对象销毁后立即使用该对象。
更糟糕的是,旧对象的内存区域现在可能部分或完全属于应用程序中的新对象(或其他类型的数据),因此通过访问幽灵对象的成员,您可能会随机更改这些新对象中的字节. 这可能会在您的应用程序生命周期的任何后期导致细微的随机故障、错误和崩溃。
但是您不必关心这些细节,因为无论如何您都不会使用已释放的对象。
(或者,这样想:如果你使用一个被释放的对象,你就违反了合同,基本上任何事情都可能发生。一种可能性是根本没有任何“奇怪”的事情发生,但这只是运气不好。)
推荐阅读
- c# - 为什么允许我们从线程池线程修改表单标题?
- c# - 如何防止 Razor 视图中自定义 Html 助手的摆动?
- ibm-cloud - 如何将 MQTT Dashboard、IOT MQTT Panel 等可用应用程序连接到 IBM BlueMix
- firebase - 如何在 Firebase 测试实验室中执行指定测试
- python - Python 函数仅在 Django 上的服务器启动后工作一次
- ios - 如何附加到 testflight 应用程序中发布的调试?
- python - ValueError: X.shape[1] = 8 应该等于 1500,训练时的特征数
- javascript - 如何读取辣椒机器人的二维码?
- asp.net - 单击按钮时未填写runat服务器字段?
- java - JavaFX:使用控制器更改场景