Einzelnen Beitrag anzeigen

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