![]() |
Freigabe eines Objektes wenn keine Referenz mehr drauf zeigt
Hiho
Ich programmiere zur Zeit nen kleines 2D Spiel und benutze dafür Vektoren für die Position, Bewegung und Beschleunigung der einzelnen Spielelemente. Für die unterschiedlichen Vektorenrechnungen brauch ich teilweise temporäre Vektoren für die Zwischenrechnungen und habe solangsam kein Bock mehr pro Methode wo Vektoren benötigt werden ca. 5 - 10 temporäre Vektorvariablen zu deklarieren und mich um die Freigabe etc. zukümmern. Mal der Aufbau der Vektorenklasse
Delphi-Quellcode:
Alle class function XYZ: TVector2D; geben eine neue Instanz eines Vektors zurück, was mir pro temporäre Zwischenrechnung die Initialisierung einer neuen Instanz erspart.
TVector2D = class
constructor Create(); overload; constructor Create(_X, _Y: single); overload; constructor Create(AVec: TVector2D); overload; private FX, FY: single; function FGetdX: integer; function FGetdY: integer; function FGetLength: single; procedure FSetX(value: single); procedure FSetY(value: single); procedure FSetLength(value: single); public procedure Normalize(); overload; class function Normalize(AVector: TVector2D): TVector2D; overload; procedure Add(AVector: TVector2D); overload; class function Add(AVector: TVector2D; AnotherVector: TVector2D): TVector2D; overload; procedure Sub(AVector: TVector2D); overload; class function Sub(AVector: TVector2D; AnotherVector: TVector2D): TVector2D; overload; procedure Multiply(ASkalar: single); overload; class function Multiply(AVector: TVector2D; ASkalar: single): TVector2D; overload; procedure Divide(ASkalar: single); overload; class function Divide(AVector: TVector2D; ASkalar: single): TVector2D; overload; procedure FromWinkel(AWinkel: single; ALength: single); class function FromWinkelEx(AWinkel: single; ALength: single): TVector2D; function WinkelBetween(AVector: TVector2D): single; overload; class function WinkelBetween(AVector, AnotherVector: TVector2D): single; overload; class function Distance(AVector, AnotherVector: TVector2D): single; function Dot(AnotherVector: TVector2D): single; overload; class function Dot(AVector, AnotherVector: TVector2D): single; overload; property X: single read FX write FSetX; property Y: single read FY write FSetY; property dX: integer read FGetdX; property dY: integer read FGetdY; property Length: single read FGetLength write FSetLength; end; Momentan isses nun so, das ich wie gesagt tierisch auf die Freigabe der benutzten Temporären Vektoren achten muss. Ma nen simples Beispiel.
Delphi-Quellcode:
Wie gesagt, war das nun ein einfaches Beispiel und in der Regel brauch ich pro Methode 5 - 10 temporäre Vektoren um jeweils die Zwischenergebnisse abzuspeichern und dann freizugeben.
procedure DoSomething(EinSpielelement, EinAnderesSpielelement: TEntity);
var tempVec: TVector2D; begin // Wir wollen die Distanz zwischen den beiden Spielelementen rauskriegen // Dazu subtrahieren wir den Ortsvektor des einen Elementes vom anderen // und ermitteln denn aus dem Vektor Ergebnis die Länge // Ortsvektor wird bei TEntity unter "Pos" gespeichert tempVec := TVector2D.Sub(EinSpielelement.Pos, EinAnderesSpielelement.Pos); result := tempVec.Length; tempVec.Free(); end; Ich würd das viel lieber so haben:
Delphi-Quellcode:
Nur leider endet das dann im Speicherleck. Ich hab schon über eine Globale Liste aller Vektoren nachgedacht, wo ich nach jedem Spielezyklus alle ungenutzten Vektoren kille, nur leider müßte ich dann alle Nase lang mich drum kümmern um die Vektoren als "genutzt" zu kennzeichnen damit die Liste mir nicht versehentlicherweise nen genutzten Vektor freigibt und ich dann in die AVs rassel. Das setzen der Vektoren auf "gebraucht" würde aber eigentlich genauso viel Arbeit machen wie das freigeben, sprich ich wär nicht weiter.
procedure DoSomething(EinSpielelement, EinAnderesSpielelement: TEntity);
var tempVec: TVector2D; begin // SPEICHERLECK WEIL DIE INSTANZ VON TVECTOR2D NICHT WIEDER FREIGEGEBEN WIRD result := TVector2D.Sub(EinSpielelement.Pos, EinAnderesSpielelement.Pos).Length; end; Gibt es irgendwie in Delphi eine Möglichkeit automatisch ein Objekt killen zu lasen, sobald keine Referenz mehr drauf besteht wie z.b. in C# mit dem Garbage Collector ? MfG - Thebe |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
COM-Objekte machen sowas. Die haben Referenzzähler. Der Overhead ist zwar "relativ" gering, aber doch spürbar, insbesondere bei zeitkritischen Anwendungen.
Delphi-Quellcode:
Var
x,y : IMyComInterface; Begin x := GetObject; // object wird alloziiert. RefCount = 1 x := Nil; // Referenz wird überschrieben, RefCount = 0==> Free x := GetObject; // object wird alloziiert. RefCount = 1 y := x; // Refcount = 2 x := Nil; // Referenz wird überschrieben, RefCount = 1 End; // Ende des Gültigkeitsbereichs von x und y. Delphi generiert den entsprechenden Code, um Speicherlecks zu vermeiden |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
Das hört sich doch schonmal klasse an..
Nur leider hab ich nicht wirklich Ahnung wie man das in meine Klasse einfädelt. Ich schätze mal das Funktioniert über Interfaces, nur von denen hab ich so gut wie null Ahnung. Kann man mir vielleicht nen Denkanstoß geben in wie fern das funktionieren soll ? |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
Prinzipiell ist das fast so wie eine Klasse. Schau mal unter TInterfaced Object. Sooo die Ahnung hab ich aber auch nicht.
geh mal zu sourceforge und saug dir das 'DWS', ein klasse Delphi-Interpreter!!! Deren Sourcecode ist voll mit Interface-Deklarationen. Als Nebeneffekt hast Du gleich mal soeben einen echt geilen Delphi-Interpreter! |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
Hallo Thebe,
statt class functions solltest du vielleicht lieber Konstruktoren nutzen, aber das musst du selbst wissen. Zu dem Problem selbst: Nur um zwei Vektoren zu subtrahieren, brauchst du ja keine Klasse, da würde eine einfache Funktion voll und ganz genügen. Da es aber nur ein kleines Beispiel ist, kann ich das nicht so pauschal behaupten. Ansonsten würde ich das vielleicht so machen: 1) Listenklasse implementieren oder TObjectList nutzen 2) Beim Beginn einer Routine, die Vektoren nutzt, wird die Klasse erstellt, bei deren Ende wieder zerstört. 3) Alle innerhalb der Routine genutzten Vektoren werden z.B. auf folgende Weise erstellt:
Delphi-Quellcode:
4) Die Add-Methode erstellt jeweils eine neue Vektoren-Instanz, fügt sie der Liste hinzu und gibt sie zurück.
procedure MyProc();
var Lst: TMyListClass; V1, V2: TMyVectorClass; begin Lst.Create(); [...] V1 := Lst.Add(); V2 := Lst.Add(); [...] Lst.Free(); end; 5) Durch Lst.Free() werden alle Objekte in der Liste freigegeben. Ich hoffe, ich konnte dir wenigstens einen Denkanstoß geben ;) Gruß, Marco |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
Hallo
Ich würde auch eher mit nicht object orientierten functionen arbeiten um längen, winkel oder ähnliches zu ermitteln. DerDan |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
Soo
ich hab das nu ma mit den Interfaces probiert, nur so wirklich funkt das nicht. Testklasse:
Delphi-Quellcode:
Dazu wird an einer bestimmten Stelle folgender Code aufgerufen:
unit uTestClass;
interface uses SysUtils; type TTestClass = class(TObject, IInterface) protected FRefCount: integer; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; public class function NewInstance: TObject; override; property RefCount: Integer read FRefCount; end; implementation { TTestClass } function TTestClass._AddRef: Integer; begin FRefCount := RefCount + 1; Result := FRefCount; // [X] HALTEPUNKT end; function TTestClass._Release: Integer; begin FRefCount := FRefCount - 1; Result := FRefCount; // [X] HALTEPUNKT if Result = 0 then Destroy; end; function TTestClass.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE; end; class function TTestClass.NewInstance: TObject; begin Result := inherited NewInstance; TTestClass(Result).FRefCount := 1; end;
Delphi-Quellcode:
Tja, was soll ich sagen, weder _AddRef noch _Release werden aufgerufen. So funktioniert das schon mal auf alle Fälle nicht.
var
t: TTestClass; begin t := TTestClass.Create(); t := nil; end; @Marphy: Das Ding bei deinem Vorschlag ist ja, das erstma die Liste initialisiert werden muss. Dann muss ich ja auch noch alle Nase lang die temp. Variablen deklarieren um damit zu arbeiten. Wenn ich die nicht deklariere, dann komm ich ja nicht über das "MyList.Add()" hinaus. Danach muss ich dann auch noch mich zumindest um das Freigeben der Liste kümmern. In etwa bin ich wieder bei +/- 0, ich muss weiterhin die Variablen deklarieren und ich muss weiterhin mich ums freigeben kümmern (auch wenn ein wenig einfacher nu). Ich finde, da lohnt sich das neuschreiben einer TVectorList Klasse noch nicht mal plus das umschreiben des bisherigen Codes für das Ergebnis. Aber danke für den Denkanstoß! :) MfG - Thebe |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
Ich glaube du solltest eher das Singleton Pattern implementieren, wenn du einen Referenzzähler haben möchtest.
Das würde dann eher so aussehen :
Delphi-Quellcode:
Musst du gucken, ob du irgendwas nebenläufig machst, dann wäre natürlich ne Criticalsection nicht unwichtig. Für Fehler im Code vorab sorry, hab es gerade nur im Browser gemacht, ohne Syntaxcheck.
unit UTestClass;
interface uses ....; type TTestClass = class(TObject) protected refCount : Integer; constructor create; public class function getInstance : TTestClass; class procedure releaseInstance; end; implementation var GlobalInstance : TTestClass; class function TTestClass.getInstance; begin if not assigned(GlobalInstance) then begin GlobalInstance := TTestClass.create; end; // if assigned(GlobalInstance) inc(GlobalInstance.refCount); result := GlobalInstance; end; // class function TTestClass.getInstance; class procedure TTestClass.releaseInstance; begin if assigned(GlobalInstance) then begin dec(GlobalInstance.refCount); if (GlobalInstance.refCount < 1) then begin GlobalInstance.Free; end; // if (GlobalInstance.refCount < 1) end; // if assigned(GlobalInstance) end; // class procedure TTestClass.releaseInstance; constructor TTestClass.create; begin self.refCount := 0; end; // constructor TTestClass.create; |
Re: Freigabe eines Objektes wenn keine Referenz mehr drauf z
Tja, was soll ich sagen, weder _AddRef noch _Release werden aufgerufen. So funktioniert das schon mal auf alle Fälle nicht. Dafür muß die Variable als IInterface und nicht als TTestObject deklariert werden. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:36 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz