Home » Tipps & Tricks » Object Pascal » Sonstiges » Objekt sich selbst freigeben lassen

Objekt sich selbst freigeben lassen

Manchmal möchte man, dass sich ein Objekt gewissermaßen selbst freigibt. Beispielsweise könnte sich ein Button bei einem Klick selbst entfernen.Das Problem ist hierbei, dass es zu einer Zugriffsverletzung kommen kann, wenn sich ein Objekt in seiner eigenen Methode freigibt: Nämlich dann, wenn nach der Methode auf Felder des freigegebenen Objekts zugegriffen werden. Da man selbst ja nie wissen kann, was intern nach z.B. einem Event abläuft, kann es sehr riskant sein, wenn man in so einem Event das Objekt, welches es ausgelöst hat, freigeben will. Es sei denn, man ist sich absolut sicher, dass nach dem Free keine Zugriffe mehr auf Felder des Objekts stattfinden. Dies ist aber nicht zu empfehlen und gilt als sehr unsauber.
Mit dieser Unit kann man dem entgegenwirken: Ein Objekt wird mit PostFree freigegeben. Aber nicht sofort – erst dann, wenn alle nach dem PostFree folgenden Routinen fertig sind. Außerdem kann man verhindern, dass eine Zugriffsverletzung auftritt, wenn ein Objekt zweimal über PostFree desselben FreeManagers freigegeben wird.

Warnung: Ein Aufruf der Prozedur Application.ProcessMessages bringt den FreeManager dazu, vorzeitig alle zu zerstörenden Objekte freizugeben. Ein Aufruf dieser Prozedur sollte also nicht nach einem PostFree innerhalb der selbigen Methode stattfinden. Darum ist es besser, PostFree erst am Ende der Methode des zu zerstörenden Objekts aufzurufen.
Außerdem muss beachtet werden, dass diese Unit nicht Thread-safe ist!

Damit sich ein Objekt selbst freigeben kann, muss sich die Unit „hdFreeUtilities“ zunächst im Delphi-Suchpfad befinden. Um die Unit dann benutzen zu können, muss sie in der Uses-Deklaration der aufrufenden Unit referenziert sein.Dann kann ein Objekt einfach mit der Funktion PostFree freigegeben werden.Beispiel:

uses hdFreeUtilities;
[...]
procedure TForm1.Button1Click(Sender: TObject);
begin
  PostFree(Sender); //Entfernt den Butten, der diese Methode aufruft
end;

Folgende Unit muss einfach kopiert und als „hdFreeUtilities.pas“ gespeichert werden:

unit hdFreeUtilities;

interface

uses
  Messages, Windows, Classes;

const
  // Nachricht, die den FreeManager dazu veranlasst, ein Objekt freizugeben
  WM_FREE_OBJECT = WM_USER + 0;

type
  // Der FreeManager - Eine Instanz wird bereits bei Unit-Initialisierung erzeugt.

  TFreeManager = class(TObject)
  private
    FFreeList: TList;

    FWndHandle: HWND;
  protected
    procedure WndProcFreeObj(var Msg: TMessage); message WM_FREE_OBJECT;

  public
    function PostFree(AObject: TObject): Boolean;

    procedure FreeObjects;

    constructor Create();
    destructor Destroy; override;
  end;

  // Alle hiervon abgeleiteten Objekte können sich über die eigene Methode PostFree selbst freigeben

  TOwnFreeObject = class(TObject)
  public
    procedure PostFree;
  end;

  // Postfree kann auch unabhängig von bereits verwendeten FreeManagern benutzt werden:
  // es wird dann der Unit-lokale FreeManager verwendet

function PostFree(AObj: TObject): Boolean;

implementation

var
  FreeManager: TFreeManager; // Lokaler FreeManager

  ///
  /// Diese Funktion gibt das übergebene Objekt nach einer gewissen Zeit frei.
  ///
  ///  TFreeManager.PostFree

function PostFree(AObj: TObject): Boolean;
begin
  Result := FreeManager.PostFree(AObj);
end;

{ TFreeManager }

constructor TFreeManager.Create;
begin
  inherited;

  FFreeList := TList.Create();
  FWndHandle := Classes.AllocateHWnd(WndProcFreeObj);
end;

destructor TFreeManager.Destroy;
begin
  FreeObjects;
  FFreeList.Free;
  Classes.DeallocateHWnd(FWndHandle);

  inherited;
end;

///
/// Mit dieser Funktion wird ein angegebenes Objekt nach einer gewissen Zeit freigegeben.
/// Ein Objekt kann sich so also in seiner eigenen Routine freigeben,
/// allerdings wird das Objekt dann erst freigegeben, wenn alle Routinen abgearbeitet wurden.
///
///  Das freizugebende Objekt
///
/// False, wenn das Objekt in diesem FreeManager bereits freigegeben wurde
/// True, wenn alles erfolgreich war
///

function TFreeManager.PostFree(AObject: TObject): Boolean;
begin
  Result := Assigned(AObject) and (FFreeList.IndexOf(AObject)  -1) then
  begin
    FFreeList.Delete(Index);
    AObject.Free;
  end;
end;

{ TOwnFreeObject }

///
/// Über diese Prozedur kann sich das von TOwnFreeObject abgeleitete Objekt selbst freigeben.
///

procedure TOwnFreeObject.PostFree;
begin
  FreeManager.PostFree(Self)
end;

///
/// Initialierung und finalisierung des lokalen FreeManagers
///

initialization
  FreeManager := TFreeManager.Create();

finalization
  FreeManager.Free;

end.