AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Referenzen von Objekte

Ein Thema von ngott2 · begonnen am 6. Aug 2015 · letzter Beitrag vom 6. Aug 2015
Antwort Antwort
Seite 1 von 2  1 2      
ngott2

Registriert seit: 26. Aug 2013
81 Beiträge
 
Delphi 10 Seattle Professional
 
#1

Referenzen von Objekte

  Alt 6. Aug 2015, 13:14
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
  Mit Zitat antworten Zitat
Benutzerbild von BUG
BUG

Registriert seit: 4. Dez 2003
Ort: Cottbus
2.094 Beiträge
 
#2

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 13:17
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?
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 13:17
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.
  Mit Zitat antworten Zitat
Benutzerbild von BUG
BUG

Registriert seit: 4. Dez 2003
Ort: Cottbus
2.094 Beiträge
 
#4

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 13:19
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
  Mit Zitat antworten Zitat
ngott2

Registriert seit: 26. Aug 2013
81 Beiträge
 
Delphi 10 Seattle Professional
 
#5

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 13:28
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
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 13:39
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
Stimmt, das wäre natürlich seeehr unangenehm. Dann besser doch den Code sichten und nur in der unit wo das Create stattfindet auch das Free ausführen.
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.343 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 13:41
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.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von BUG
BUG

Registriert seit: 4. Dez 2003
Ort: Cottbus
2.094 Beiträge
 
#8

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 13:58
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.

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.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#9

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 14:51
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 TObjectList mit OwnsObjects ).

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

Somit kann man sich auch einen 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 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.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.143 Beiträge
 
Delphi 10.3 Rio
 
#10

AW: Referenzen von Objekte

  Alt 6. Aug 2015, 14:56
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?
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:54 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