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.
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.
When the variable is really global (i.e. is declared under var
in the interface
section of the unit), it should be created and destroyed in the initialization
and finalization
section of the unit itself.
. . .
var
procedures: TObjectDictionary<string, TCallback>;
. . .
initialization
procedures:= TObjectDictionary<string, TCallback>.Create([doOwnsValues]);
finalization
procedures.Free;
When your variable belongs to the form class itself, you should create the dictionary in the form's OnCreate
event.
. . .
public
procedures: TObjectDictionary<string, TCallback>;
. . .
procedure TForm1.FormCreate(Sender: TObject);
begin
procedures:= TObjectDictionary<string, TCallback>.Create([doOwnsValues]);
end;
Free the dictionary in the form's OnDestroy
event:
procedure TForm1.FormDestroy(Sender: TObject);
begin
procedures.Free;
end;
Furthermore, if you want the variable belonging to the class to be accessed without the need of an instance of the class itself (this is referred as a static variable in many programming languages), you can declare the dictionary as a class var
and optionally access it through a class property
; in such a case, it's better to create and destroy the collection in the class constructor
and in the class destructor
.
. . .
TMyClass = class
private
class constructor Create;
class destructor Destoy;
public
class var procedures: TObjectDictionary<string, TCallback>;
end;
. . .
class constructor TMyClass.Create;
begin
procedures := TObjectDictionary<string, TCallback>.Create([doOwnsValues]);
end;
class destructor TMyClass.Destoy;
begin
procedures.Free;
end;
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.