Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi interface reference count mechanism

Tags:

delphi

Indeed there is a lot of stuff online about this but more I read more confuse I am. I have written a component called Combinatorics that does some math probability stuff. The code is pretty short and easy because I don't want it to be complicated. I am doing a little preview here:

//Combinatorio.pas
type
 ICombinatorio = interface
  function getSoluzioni(): integer; //soluzioni means "Solutions"
  function getFormula(): string;
 end;

//ImplCombinatorio.pas
type

 TCombinazioni = class(TInterfacedObject, ICombinatorio)
  private
   n, k: integer;
   ripetizione: boolean;
   function fattoriale(const x: integer): integer;
  public
   constructor Create(const n, k: integer; const ripetizione: boolean);
   function getSoluzioni(): integer;
   function getFormula(): string;
 end;

 TDisposizioni = class(TInterfacedObject, ICombinatorio)
  private
   n, k: integer;
   ripetizione: boolean;
   function fattoriale(const x: integer): integer;
  public
   constructor Create(const n, k: integer; const ripetizione: boolean);
   function getSoluzioni(): integer;
   function getFormula(): string;
 end;

 TPermutazioni = class(TInterfacedObject, ICombinatorio)
  private
   n: integer;
   k: string;
   ripetizione: boolean;
   function fattoriale(const x: integer): integer;
  public
   constructor Create(const n: integer; const k: string; ripetizione: boolean);
   function getSoluzioni(): integer;
   function getFormula(): string;
 end;

You don't need to see how functions and procedures are implemented, it's not important for the question (and you can easily imagine what they do).


This is my first component ever, I have compiled and installed it and it works. However I cannot understand something.

unit TCombinatorio;

interface

uses
  System.SysUtils, System.Classes, Combinatorio, ImplCombinatorio;

type
 cCombinatorio = (cNull = 0, cDisposition = 1, cPermutation = 2, cCombination = 3);

type
 TCombinatorics = class(TComponent)
 strict private
  { Private declarations }
  Fn, Fk: integer;
  FRep: boolean;
  FType: cCombinatorio;
  FEngine: ICombinatorio;
  procedure Update;
 public
  { Public declarations }
  constructor Create(AOwner: TComponent); override;
  function getSolution: integer;
  function getFormula: string;
 published
  property n: integer read Fn write Fn;
  property k: integer read Fk write Fk;
  property kind: cCombinatorio read FType write FType default cNull;
  property repetitions: boolean read FRep write FRep;
end;

procedure Register;

implementation

procedure Register;
begin
 RegisterComponents('RaffaeleComponents', [TCombinatorics]);
end;

{ TCombinatorics }

constructor TCombinatorics.Create(AOwner: TComponent);
begin

 inherited Create(AOwner);
 Fn := 0;
 Fk := 0;
 FType := cNull;
 repetitions := false;

end;

function TCombinatorics.getFormula: string;
begin
 Update;
 Result := FEngine.getFormula;
end;

function TCombinatorics.getSolution: integer;
begin
 Update;
 Result := FEngine.getSoluzioni;
end;

procedure TCombinatorics.Update;
begin

 case FType of
  cDisposition:
   FEngine := TDisposizioni.Create(n, k, repetitions);
  cPermutation:
   FEngine := TPermutazioni.Create(n, '', repetitions);
  cCombination:
   FEngine := TCombinazioni.Create(n, k, repetitions);
  cNull:
   raise Exception.Create('You have to select a type.');
 end;

end;

end.

Look at the Update; procedure. I have created that because when the user drops the component ( link ) in the form he has to setup in the object inspector (or with the code somewhere) 3 important parameters required in the constructor.

Since FEngine: ICombinatorio I can assign to it a class (TCombinazioni, TDisposizioni or TPermutazioni) without try finally because there is the ref count mechanism. I am not sure if I have coded this properly. Suppose that:

  1. The user selects cDisposition and does a calculation
  2. The user selects cDisposition (different values) and does a calculation
  3. The user selects cPermutation and does a calculation

I am always working on the FEngine. How does the ref count go to zero? Does it go to zero when the form (and the component) destroys? I hope I have explained well what I don't understand. The FEngine is a private variable and I assing to it at runtime different classes (calling the Create). Does the ref count go to 0 when the form destroys or when a new class is assigned?

I coded it like above because nick hodges did that in his book and I trust him of course but I'd like to know what I do.

like image 592
Raffaele Rossi Avatar asked Jul 18 '17 20:07

Raffaele Rossi


Video Answer


1 Answers

Based on the code that can be seen, the first time Update is called, a new implementor of ICombinatorio is created and assigned to FEngine; the reference count will be 1. The following times that Update is called, another new instance of ICombinatorio implementor will be created (its reference count will be 1) and is assigned to FEngine. The previous implementor instance that FEngine pointed to will have its reference count decremented; if it is zero, then it will be destroyed. (It probably will be based on your code sample).

Also, when the destructor of the component is called (when the owning Form is destroyed), the implicit instance clean-up code will set FEngine to nil, which will decrement the reference count (and, based on your sample, will be destroyed).

So, based on your code sample, I would expect your code will work properly; cleanly instanciating and destroying the ICombinatorio interfaced objects.

like image 97
Dave Olson Avatar answered Sep 17 '22 12:09

Dave Olson