Link to home
Start Free TrialLog in
Avatar of Mutley2003
Mutley2003

asked on

function returning TInterfacedObject

It is convenient, in my code, for me to have a function returning an instance of an object without me having to worry about managing that instance .. in other words, I am looking for garbage collection.

I don't know much about interfaces, but I thought the following might work
type
Tfred=class(TinterfacedObject)
  private
  function getAsString: String;
public
  property AsString : String read getAsString;
end;


function Tfred.getAsString: String;
begin
result := inttostr(refCount);
end;

type
 Ttom = class
public
  function aFred: Tfred;
end; // Ttom

 function Ttom.aFred: Tfred;
 begin
  result :=Tfred.create;
  // never cleaned up
 end;


or even
function myFred:Tfred; begin result:=Tfred.create; end;


procedure TForm1.btnTestReferencecountingClick(Sender: TObject);
var k: integer; tom : TTom;

begin
tom := Ttom.create;
for k:=1 to 100 do begin
dsmemo1.out([tom.afred.AsString, myFred.AsString]);
end;
tom.free;
end;

Is this a correct usage of TinterfacedObject?

I don't think reference counting is going to come into it, but will all my instances of Tfred be released at the end of the calling procedure??

Also, I don't understand how the cleanup can happen correctly without Delphi maintaining some sort of internal list (in which case there would be a hidden overhead, compared to managing the lifetimes yourself). I understand the reference count is maintained with the object, but somehow - at run time - the app must tell the object that it is no longer in scope ..

enlightenment and code correction sought, please
Avatar of mocarts
mocarts

Hi, Mutley :)
Your code will work, but you still need to free your objects, since reference counting is used only to interfaces.
TInterfaceObject is helper object which impelements AddRef, Release methods of IInterface. You should declare and implement your IFred interface to use reference counting..
here will be reworked code:

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  end;

  IFred = interface
    function AsString: string;
  end;

  TFred = class(TInterfacedObject, IFred)
  public
    destructor Destroy; override;
    function AsString: string;
  end;

  TTom = class
    function GetFred: IFred;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TFred }

function TFred.AsString: string;
begin
  Result := inttostr(RefCount);
end;

destructor TFred.Destroy;
begin
  ShowMessage('Destroing');
  inherited;
end;

// simple function to get Fred
function GetFred: IFred;
begin
  Result := TFred.Create;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Tom: TTom;
  i: integer;
begin
  tom := TTOm.Create;
  try
    for i := 0 to 10 do
     // scope of IFred is limited only to this line
     //(actually there is two separate TFred interface instances used)
      Memo1.Lines.Add(Tom.GetFred.AsString + '; '+ GetFred.AsString);
  finally
    tom.Free;
  end;
end;

{ TTom }

function TTom.GetFred: IFred;
begin
  Result := TFred.Create;
end;

wbr, mo.
Delphi's reference counting is, in my experience, not useful.  If you add an interface object to a list, for example, the reference count is not updated, so your objects may disappear "randomly", or fail yto be garbage collected and the reason not be apparent.
Even in Java, you have to pay careful attention to the GC and the memory model, or it works against you rather than for you.  Java uses a access graph rather than a reference count, which is mroereliable, and even it has problems.

In Delphi, it is far better to ensure that you free everything than to depend on the GC model.
ASKER CERTIFIED SOLUTION
Avatar of swift99
swift99

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Mutley2003

ASKER

swift99

ok, I am convinced. With only a little more work I can use the traditional approach, and with results like the ones you have obtained, I see no reason to venture into the uncharted waters of interfaces and garbage collection

thanks
The implementation of the "interface" concept in Delphi was tied in to COM.  This was a deliberate decision because of the way that Windows architecture was going.  It is not fair to write off the interfaces approach entirely

Delphi Interfaces are still useful in two places:
1. any interaction through the COM or ActiveX API - say a DLL that implements a COM object for use by an outsode program
2. operations where performance is not an issue, and you can control the refernces yourself - for example the DUnit testing framework

In Java, the designers did not labour under the need to conform to a M$oft architecture direction of the week, so interfaces are implemented as a parallel VMT (array of pointers) and are very light weight.

Interfaces and garbage collection should be considered as two independent questions, even though they have been rolled together in the Delphi model.  I think that the Delphi architects recognize this flaw, but too much code has been written for this blurred paradigm for them to arbitrarily change it and break their customers' code base.