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.