delphidictionarymemory-leakstdictionary

Delphi dictionary freeing


I implemented the following class:

type
  TUtilProcedure = procedure(var AJsonValue: TJSONObject);

  TCallback = class
  private
    FName: string;
    FProcedure: TUtilProcedure;
    FAnnotation: string;
  public
    constructor Create(AName: string; AProcedure: TUtilProcedure; AAnnotation: string); overload;
    constructor Create(ACallback: TCallback); overload;
    property Name: string read FName;
    property Proc: TUtilProcedure read FProcedure;
    property Annotation: string read FAnnotation;
 end;

Then I have a global variable:

procedures: TDictionary<string, TCallback>;

In OnFormActivate procedure I initalize the procedures variable:

procedures := TDictionary<string, TCallback>.Create();
procedures.Add('something', TCallback.Create('sth', @proc, 'annotation')); 
// ....

And then in OnFormClose I free it:

procedures.Clear;
procedures.Free;

Does my code leak memory? If so what is the correct way to free the dictionary? From what I know iteration is not good idea.


Solution

  • The code leaks memory because the objects contained in a TDictionary are not automatically freed.

    If you need to store objects in a dictionary, the adoption of a TObjectDictionary represents a better approach.

    If you want the objects contained in the dictionary to be automatically freed, use the doOwnsValues flag while creating the instance of the collection.


    TCallback = class
      private
        FName: string;
        FProcedure: TUtilProcedure;
        FAnnotation: string;
      public
        constructor Create(AName: string; AProcedure: TUtilProcedure; AAnnotation: string); overload;
        constructor Create(ACallback: TCallback); overload;
        property Name: string read FName;
        property Proc: TUtilProcedure read FProcedure;
        property Annotation: string read FAnnotation;
    end;
    

    As a side note, the TCallback class does not need to specify a destructor because only owns two strings and a pointer to a procedure. And so the default destructor inherited from TObject suffices.