Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Referenzen von Objekte (https://www.delphipraxis.net/186132-referenzen-von-objekte.html)

ngott2 6. Aug 2015 12:14

Referenzen von Objekte
 
Hallo,
wir sind gerade dabei einer unser Tools zu Optimieren. Dort gibt es Objekte die nicht freigeben wurden.
Dies tun wir nun. Nun kommt es manchmal vor das ein anderes Objekt ein Verweis auf das von uns freigegebene Objekt hat(Objekt zeiger).
Nun wollen wir die Referenzen zählen um die Doppelte Freigabe zu verhindern. Kennt einer von euch ein Entwurfsmuster was sich für diesen Zweck eignet ?

Gruß,
ngott2

BUG 6. Aug 2015 12:17

AW: Referenzen von Objekte
 
Ich mag ja das Design der std::shared_pointer in der C++ Standard-Library, allerdings lässt sich das Smart-Pointer-Konzept afaik nicht so leicht nach Delphi übertragen. Spricht etwas gegen Interfaces?

Bjoerk 6. Aug 2015 12:17

AW: Referenzen von Objekte
 
Wenn du die Objekte konsequent mit FreeAndNil oder Dingens.Frree und Dingens:= nil "zerstörst" brauchst du keinen Zähler. Free prüft auf nil.

BUG 6. Aug 2015 12:19

AW: Referenzen von Objekte
 
Zitat:

Zitat von Bjoerk (Beitrag 1311273)
Wenn du die Objekte konsequent mit FreeAndNil oder Dingens.Frree und Dingens:= nil "zerstörst" brauchst du keinen Zähler. Free prüft auf nil.

Das Problem ist dann natürlich, wenn man 2 Zeiger auf ein Objekt hat :stupid:

ngott2 6. Aug 2015 12:28

AW: Referenzen von Objekte
 
Zitat:

Zitat von BUG (Beitrag 1311272)
Spricht etwas gegen Interfaces?

Wie müsste man das Implementieren ?

Code:
  TKfmPreis= class
                  public
                    Nummer : Integer
                    preis : single
                    // Es stehen hier noch einige Variablen mehr aber die sind jetzt nicht wichtig
                   
                   
                    constructor Create;
                    destructor Destroy;
                    function GetCopy : TKfmPreis;
               end;
getCopy gibt ein Kopie von sich selbst zurück

Bjoerk 6. Aug 2015 12:39

AW: Referenzen von Objekte
 
Zitat:

Zitat von BUG (Beitrag 1311274)
Zitat:

Zitat von Bjoerk (Beitrag 1311273)
Wenn du die Objekte konsequent mit FreeAndNil oder Dingens.Frree und Dingens:= nil "zerstörst" brauchst du keinen Zähler. Free prüft auf nil.

Das Problem ist dann natürlich, wenn man 2 Zeiger auf ein Objekt hat :stupid:

Stimmt, das wäre natürlich seeehr unangenehm.:oops: Dann besser doch den Code sichten und nur in der unit wo das Create stattfindet auch das Free ausführen.

stahli 6. Aug 2015 12:41

AW: Referenzen von Objekte
 
Es gibt da sicher unterschiedliche Ansätze.

Mit Interfaces hättet Ihre eine automatisierte Lösung. Allerdings könnte der Projektumbau recht aufwendig werden.

Außerdem könnt Ihr die Lebenszeit der Objekte nicht mehr konkret regeln.

Wenn Ihr ein Objekt freigeben wollt (bzw. ein Interface mit MyIntf := nil nicht mehr referenziert) kann das Objekt dahinter noch weiter leben bis die letzte Referenz darauf entfernt wird.
Gegenseitige Referenzen zwischen Interfaces können zusätzliche Probleme verursachen.

Vielleicht ist es am einfachsten, wenn Ihr einfach Referenzen zwischen Euren Objekten explizit verwaltet: ReferenzenAufMich: TObjectList.
Dann können sich die Objekte gegenseitig registrieren und wieder abmelden.

Die beste Lösung hängt wohl davon ab, wie Euer Projekt aufgebaut ist.

BUG 6. Aug 2015 12:58

AW: Referenzen von Objekte
 
Zitat:

Zitat von stahli (Beitrag 1311285)
Wenn Ihr ein Objekt freigeben wollt (bzw. ein Interface mit MyIntf := nil nicht mehr referenziert) kann das Objekt dahinter noch weiter leben bis die letzte Referenz darauf entfernt wird.

Diese Problem ist im Prinzip die (imho) angenehmere Variante von: Ich möchte mein Objekt jetzt freigeben, weiß aber nicht ob es sonst irgendwo benutzt wird. Insofern verliert man da nicht viel.

Zitat:

Zitat von stahli (Beitrag 1311285)
Gegenseitige Referenzen zwischen Interfaces können zusätzliche Probleme verursachen.

Insgesamt kann es nicht schaden, sich mal allgemein über Reference-Counting zu informieren. Man ist ja schließlich nicht der erste Mensch, der sich damit beschäftigt.

Sir Rufo 6. Aug 2015 13:51

AW: Referenzen von Objekte
 
Bei der Freigabe von Instanzen geht es ja primär um die Verantwortlichkeit. Wer räumt die Instanz aus dem Speicher. Diese Verantwortlichkeit kann man delegieren (z.B. an eine
Delphi-Quellcode:
TObjectList
mit
Delphi-Quellcode:
OwnsObjects
).

Und bei dieser Delegation muss ich natürlich berücksichtigen, dass ich diese Verantwortlichkeit delegiert habe. Eine
Delphi-Quellcode:
TObjectList
findet es total doof, wenn eine Instanz irgendwo anders freigeben wurde.

Somit kann man sich auch einen
Delphi-Quellcode:
InstanceManager
erstellen, der genau diese Verantwortung übernimmt, damit aber natürlich auch flächendeckend eingesetzt werden muss. Das kann man aber noch vernachlässigen, denn die Instanzen müssen immer irgenwie verwaltet werden, nur mischen darf ich nicht (aber das ist das gleiche Problem bei den Interfaces auch).

Hier mal so ein auf die Schnelle hingetippter
Delphi-Quellcode:
InstanceManager
Delphi-Quellcode:
unit Utils;

interface

uses
  System.Classes,
  System.Generics.Collections,
  System.SysUtils;

type
  InstanceManager = class abstract
  private
    class var _sync: TObject;
    class var _ReferenceCounter: TDictionary<TObject, Integer>;
    class constructor Create;
    class destructor Destroy;
    class function DoAquire( Instance: TObject ): Integer;
    class function DoRelease( Instance: TObject ): Boolean;
    class function GetCount: Integer; static;
  public
    class procedure Aquire( Instance: TObject ); overload;
    class function Aquire<T: class>( Instance: T ): T; overload;
    class function Exchange<T: class>( var OldInstance: T; const NewInstance: T ): Boolean;
    class procedure Release( Instance: TObject );
    class procedure ReleaseAndNil( var Instance: TObject ); overload;
    class procedure ReleaseAndNil<T: class>( var Instance: T ); overload;
    class property Count: Integer read GetCount;
  end;

implementation

{ InstanceManager }

class procedure InstanceManager.Aquire( Instance: TObject );
begin
  DoAquire( Instance );
end;

class function InstanceManager.Aquire<T>( Instance: T ): T;
begin
  DoAquire( Instance );
  Result := Instance;
end;

class constructor InstanceManager.Create;
begin
  InstanceManager._sync := TObject.Create;
  InstanceManager._ReferenceCounter := TDictionary<TObject, Integer>.Create( );
end;

class destructor InstanceManager.Destroy;
begin
  FreeAndNil( InstanceManager._ReferenceCounter );
  FreeAndNil( InstanceManager._sync );
end;

class function InstanceManager.DoAquire( Instance: TObject ): Integer;
var
  LCounter: Integer;
begin
  if not Assigned( Instance ) then
    Exit;

  TMonitor.Enter( InstanceManager._sync );
  try
    if not InstanceManager._ReferenceCounter.TryGetValue( Instance, LCounter ) then
      LCounter := 0;

    Inc( LCounter );
    InstanceManager._ReferenceCounter.AddOrSetValue( Instance, LCounter );
    Result := LCounter;
  finally
    TMonitor.Exit( InstanceManager._sync );
  end;
end;

class function InstanceManager.DoRelease( Instance: TObject ): Boolean;
var
  LCounter: Integer;
begin
  if not Assigned( Instance ) then
    Exit;

  TMonitor.Enter( InstanceManager._sync );
  try
    LCounter := InstanceManager._ReferenceCounter[ Instance ];
    Dec( LCounter );
    if LCounter = 0 then
    begin
      InstanceManager._ReferenceCounter.Remove( Instance );
      Instance.DisposeOf;
      Result := True;
    end
    else
    begin
      InstanceManager._ReferenceCounter.AddOrSetValue( Instance, LCounter );
      Result := False;
    end;
  finally
    TMonitor.Exit( InstanceManager._sync );
  end;
end;

class function InstanceManager.Exchange<T>( var OldInstance: T; const NewInstance: T ): Boolean;
begin
  Result := OldInstance <> NewInstance;
  if Result then
  begin
    ReleaseAndNil<T>( OldInstance );
    Aquire( NewInstance );
    OldInstance := NewInstance;
  end;
end;

class function InstanceManager.GetCount: Integer;
begin
  TMonitor.Enter( InstanceManager._sync );
  try
    Result := InstanceManager._ReferenceCounter.Count;
  finally
    TMonitor.Exit( InstanceManager._sync );
  end;
end;

class procedure InstanceManager.Release( Instance: TObject );
begin
  DoRelease( Instance );
end;

class procedure InstanceManager.ReleaseAndNil( var Instance: TObject );
var
  LInstance: TObject;
begin
  LInstance := Instance;
  Instance := nil;
  DoRelease( LInstance );
end;

class procedure InstanceManager.ReleaseAndNil<T>( var Instance: T );
var
  LInstance: T;
begin
  LInstance := Instance;
  Instance := nil;
  DoRelease( LInstance );
end;

end.
Und so sieht der im Einsatz aus
Delphi-Quellcode:
unit Forms.MainForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class( TForm )
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Label1: TLabel;
    Timer1: TTimer;
    procedure Button1Click( Sender: TObject );
    procedure Button2Click( Sender: TObject );
    procedure Button3Click( Sender: TObject );
    procedure Button4Click( Sender: TObject );
    procedure Timer1Timer( Sender: TObject );
    procedure FormShow( Sender: TObject );
    procedure FormHide( Sender: TObject );
  private
    FAnInstance: TObject;
    FAnotherInstance: TObject;
    procedure SetAnInstance( const Value: TObject );
    procedure SetAnotherInstance( const Value: TObject );

    procedure PresentInstanceCount;
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;

    property AnInstance: TObject read FAnInstance write SetAnInstance;
    property AnotherInstance: TObject read FAnotherInstance write SetAnotherInstance;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  Utils;

procedure TForm1.AfterConstruction;
begin
  inherited;
  ReportMemoryLeaksOnShutdown := True;
end;

procedure TForm1.BeforeDestruction;
begin
  AnInstance := nil;
  AnotherInstance := nil;
  inherited;
end;

procedure TForm1.Button1Click( Sender: TObject );
begin
  Self.AnInstance := TObject.Create;
  PresentInstanceCount;
end;

procedure TForm1.Button2Click( Sender: TObject );
begin
  Self.AnotherInstance := TObject.Create;
  PresentInstanceCount;
end;

procedure TForm1.Button3Click( Sender: TObject );
begin
  Self.AnInstance := Self.AnotherInstance;
  PresentInstanceCount;
end;

procedure TForm1.Button4Click( Sender: TObject );
begin
  Self.AnotherInstance := Self.AnInstance;
  PresentInstanceCount;
end;

procedure TForm1.FormHide( Sender: TObject );
begin
  Timer1.Enabled := False;
end;

procedure TForm1.FormShow( Sender: TObject );
begin
  Timer1.Enabled := True;
  PresentInstanceCount;
end;

procedure TForm1.PresentInstanceCount;
begin
  Label1.Caption := string.Format( 'Instances: %d', [ InstanceManager.Count ] );
end;

procedure TForm1.SetAnInstance( const Value: TObject );
begin
  InstanceManager.Exchange( FAnInstance, Value );
end;

procedure TForm1.SetAnotherInstance( const Value: TObject );
begin
  InstanceManager.Exchange( FAnotherInstance, Value );
end;

procedure TForm1.Timer1Timer( Sender: TObject );
begin
  PresentInstanceCount;
end;

end.

Mavarik 6. Aug 2015 13:56

AW: Referenzen von Objekte
 
Zitat:

Zitat von ngott2 (Beitrag 1311271)
Nun wollen wir die Referenzen zählen um die Doppelte Freigabe zu verhindern. Kennt einer von euch ein Entwurfsmuster was sich für diesen Zweck eignet ?

Interface wurde ja schon gesagt...

Wie wäre es mit einer Benachrichtigung an das Object, dass der Link weg ist?


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:28 Uhr.
Seite 1 von 2  1 2      

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