Reputation: 57
When I create TObjectDictionary with doOwnsValues and store TStringList as Value, after freeing the TObjectDictionary, I could still access the TStringList.
I modified the example given here: Example for using 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.
I expect S1 should not have been accessible. Is it memory leak? or bug?
Upvotes: 0
Views: 1219
Reputation: 108963
The string list object Sl
has been freed by the dictionary. No memory is leaked. There is no bug in Delphi's RTL (the dictionary class) here.
Hence, when you do Sl.Add('Foo added?');
you are accessing an object that has been freed, and that is a bug in your code. You mustn't do that.
Just because you didn't get any visible symptoms of the bug this time doesn't mean it isn't a bug.
A simpler example:
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;
This is a bug. But chances are you might get a 123
message box anyway.
When you free an object, its memory area is declared as available so that other data might be stored there in the future. Thus, immediately after the destruction of your object, its bytes are likely intact where you left them. But eventually they will be overwritten with new data, and as time goes by, all bytes might change. You never know, and you shouldn't know: you don't own that part of the computer's memory any more.
Of course, destroying an object might do more than simply giving up its location in memory: OS handles might be closed, temporary files might be removed, etc. Hence, there are many things that might go wrong if you use a freed object, even if you use the object immediately after its destruction.
Even worse, the memory area of your old object might now partially or completely belong to new objects (or other kinds of data) in your application, so by accessing the members of your ghost object, you might be randomly changing bytes in these new objects. This might cause subtle random malfunction and errors and crashes at any later time in your application's life.
But you need not care about such details, because you'd never use a freed object anyway.
(Or, think of it this way: if you use a freed object, you are violating a contract, and basically anything can happen. One possibility is that nothing "strange" happens at all, but that is just dumb luck.)
Upvotes: 8