NamoRamana
NamoRamana

Reputation: 57

Delphi - TObjectDictionary appears not freeing owned value

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

Answers (1)

Andreas Rejbrand
Andreas Rejbrand

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

Related Questions