![]() |
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 |
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?
|
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.
|
AW: Referenzen von Objekte
Zitat:
|
AW: Referenzen von Objekte
Zitat:
Code:
getCopy gibt ein Kopie von sich selbst zurück
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; |
AW: Referenzen von Objekte
Zitat:
|
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. |
AW: Referenzen von Objekte
Zitat:
Zitat:
![]() |
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:
mit
TObjectList
Delphi-Quellcode:
).
OwnsObjects
Und bei dieser Delegation muss ich natürlich berücksichtigen, dass ich diese Verantwortlichkeit delegiert habe. Eine
Delphi-Quellcode:
findet es total doof, wenn eine Instanz irgendwo anders freigeben wurde.
TObjectList
Somit kann man sich auch einen
Delphi-Quellcode:
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).
InstanceManager
Hier mal so ein auf die Schnelle hingetippter
Delphi-Quellcode:
InstanceManager
Delphi-Quellcode:
Und so sieht der im Einsatz aus
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.
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. |
AW: Referenzen von Objekte
Zitat:
Wie wäre es mit einer Benachrichtigung an das Object, dass der Link weg ist? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:43 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