Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Freigabe eines Objektes wenn keine Referenz mehr drauf zeigt (https://www.delphipraxis.net/54246-freigabe-eines-objektes-wenn-keine-referenz-mehr-drauf-zeigt.html)

Thebe 30. Sep 2005 14:00


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:
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;
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.

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:
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;
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.

Ich würd das viel lieber so haben:

Delphi-Quellcode:
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;
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.

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

alzaimar 30. Sep 2005 14:08

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

Thebe 30. Sep 2005 14:27

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 ?

alzaimar 30. Sep 2005 14:41

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!

Marphy 30. Sep 2005 14:46

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:
procedure MyProc();
var
  Lst: TMyListClass;
  V1,
  V2: TMyVectorClass;
begin
  Lst.Create();
  [...]
  V1 := Lst.Add();
  V2 := Lst.Add();
  [...]
  Lst.Free();
end;
4) Die Add-Methode erstellt jeweils eine neue Vektoren-Instanz, fügt sie der Liste hinzu und gibt sie zurück.
5) Durch Lst.Free() werden alle Objekte in der Liste freigegeben.

Ich hoffe, ich konnte dir wenigstens einen Denkanstoß geben ;)

Gruß, Marco

DerDan 30. Sep 2005 15:28

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

Thebe 30. Sep 2005 16:21

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:
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;
Dazu wird an einer bestimmten Stelle folgender Code aufgerufen:

Delphi-Quellcode:
var
  t: TTestClass;
begin
  t := TTestClass.Create();
  t := nil;
end;
Tja, was soll ich sagen, weder _AddRef noch _Release werden aufgerufen. So funktioniert das schon mal auf alle Fälle nicht.

@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

Der_Unwissende 30. Sep 2005 17:17

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:
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;
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.

LarsMiddendorf 30. Sep 2005 17:20

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 17:20 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz