Reputation: 3238
Delphi XE6 - I am using TObjectDictionary, with a custom class. I create, add, and later Free. It I do JUST that, everything is fine. If I do a "TryGetValue", I get an "Invalid Pointer Operation" on my free. The TryGetValue works fine, gives the proper result, but causes an issue later...
Here is my code.
type
TWordRec = class
public
RemoveAlways: Boolean; // Is this CORP LLC, etc?
RemoveRestricted: Boolean;
Replace: Boolean;
ReplaceWith: string;
Constructor Create(B1, B2, B3: Boolean; S1: String); overload;
end;
...
constructor TWordRec.Create(B1, B2, B3: Boolean; S1: String);
begin
self.RemoveAlways := B1; // Is this CORP LLC, etc?
self.RemoveRestricted := B2;
self.Replace := B3;
self.ReplaceWith := S1;
end;
I add my test code on a button...
procedure TForm1.Button1Click(Sender: TObject);
var
CurrentDictEntry : TWordRec;
FoundIt : Boolean;
begin
ReportMemoryLeaksOnShutdown := True;
WordDict := TObjectDictionary<String, TWordRec>.Create([doOwnsValues]);
WordDict.Add('CO', TWordRec.Create(True, False, False, ''));
WordDict.Add('CORP', TWordRec.Create(True, False, False, ''));
WordDict.Add('CORPORATION', TWordRec.Create(True, False, False, ''));
CurrentDictEntry := TWordRec.Create(False, False, False, '');
// If I remove next two code lines, everything is fine
// With these 2 lines, I get error ON WordDict.Free;
FoundIt := WordDict.TryGetValue('CORP', CurrentDictEntry);
if FoundIt = True then ShowMessage('Found');
CurrentDictEntry.Free;
WordDict.Free;
end;
Why can't I free my WordDict? If I just free WordDict (i.e. comment out CurrentDictEntry.free), the WordDict.Free works without an Invalid Pointer operation, but then I get a memory leak on exiting my app.
Upvotes: 1
Views: 727
Reputation: 612954
The main problem is here:
CurrentDictEntry.Free;
At this point, CurrentDictEntry
is a value that is contained in the container. And you cannot free the values in this container because the container owns them. If you try to free values, then the container will later try to free them again, the classic double free problem.
Once you've told the container to own the values, you must not free them.
The other problem is that you leak a TWordRec
.
CurrentDictEntry := TWordRec.Create(False, False, False, '');
FoundIt := WordDict.TryGetValue('CORP', CurrentDictEntry);
The first line creates a new object and holds a reference to it in CurrentDictEntry
. The second line then overwrites CurrentDictEntry
and now you have no references at all to the object that you just created, and it is thus leaked.
Your code should look like this:
ReportMemoryLeaksOnShutdown := True;
WordDict := TObjectDictionary<String, TWordRec>.Create([doOwnsValues]);
try
WordDict.Add('CO', TWordRec.Create(True, False, False, ''));
WordDict.Add('CORP', TWordRec.Create(True, False, False, ''));
WordDict.Add('CORPORATION', TWordRec.Create(True, False, False, ''));
FoundIt := WordDict.TryGetValue('CORP', CurrentDictEntry);
if FoundIt then
ShowMessage('Found');
finally
WordDict.Free;
end;
Upvotes: 3