Interfaces without reference counting

After reading a lot of posts on StackOverflow about the downsides of using automatic reference counting for interfaces, I started trying to manually reference the counting of each instance of the interface.

After I try to spend a full day, I give up!

Why am I getting an access violation when calling FreeAndNil (p)?

Below is a complete list of my simple device.

unit fMainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm4 = class(TForm) btn1: TButton; procedure FormCreate(Sender: TObject); procedure btn1Click(Sender: TObject); end; type IPersona = interface(IInterface) ['{44483AA7-2A22-41E6-BA98-F3380184ACD7}'] function GetNome: string; procedure SetNome(const Value: string); property Nome: string read GetNome write SetNome; end; type TPersona = class(TObject, IPersona) strict private FNome: string; function GetNome: string; procedure SetNome(const Value: string); protected function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; public constructor Create(const ANome: string); destructor Destroy; override; end; var Form4: TForm4; implementation {$R *.dfm} procedure TForm4.FormCreate(Sender: TObject); begin ReportMemoryLeaksOnShutdown := True; end; procedure TForm4.btn1Click(Sender: TObject); var p: IPersona; begin p := TPersona.Create('Fabio'); try ShowMessage(p.Nome); finally FreeAndNil(p); end; end; constructor TPersona.Create(const ANome: string); begin inherited Create; FNome := ANome; end; destructor TPersona.Destroy; begin inherited Destroy; end; function TPersona._AddRef: Integer; begin Result := -1 end; function TPersona._Release: Integer; begin Result := -1 end; function TPersona.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE; end; function TPersona.GetNome: string; begin Result := FNome; end; procedure TPersona.SetNome(const Value: string); begin FNome := Value; end; end. 
+6
source share
2 answers

Access violation occurs because FreeAndNil receives an untyped var parameter, which is expected to be an object reference. You are passing a link to an interface that does not meet the requirement. Unfortunately, you will only find out during work. This, in my opinion, is the strongest point against using FreeAndNil .

Your link counter disables lifecycle management using the interface link counting mechanism. To destroy an object, you need to call its destructor. And for this you must have access to the destructor. Your interface does not expose the destructor (and it should not). So, we can conclude that to destroy the object you need to have a link to the object.

Here are a few options:

 var obj: TPersona; intf: IPersona; .... obj := TPersona.Create('Fabio'); try intf := obj; //do stuff with intf finally obj.Free; // or FreeAndNil(obj) if you prefer end; 

Or you can do it like this:

 var intf: IPersona; .... intf := TPersona.Create('Fabio'); try //do stuff with intf finally (intf as TObject).Free; end; 
+11
source

You cannot use FreeAndNil() with a link to an interface, only a link to an object. If you left the included value of the interface link, simply assign nil to the interface link (or just exit the area) to free the object correctly, for example:

 type TPersona = class(TInterfacedObject, IPersona) strict private FNome: string; function GetNome: string; procedure SetNome(const Value: string); public constructor Create(const ANome: string); destructor Destroy; override; end; procedure TForm4.btn1Click(Sender: TObject); var p: IPersona; begin p := TPersona.Create('Fabio'); try ShowMessage(p.Nome); finally p := nil; end; end; 

But since you disabled the link counter on the interface, you need to return to using the standard object reference variables in your code, for example:

 procedure TForm4.btn1Click(Sender: TObject); var p: TPersona; intf: IPersona; begin p := TPersona.Create('Fabio'); try if Supports(p, IPersona, intf) then ShowMessage(intf.Nome); finally FreeAndNil(p); end; end; 
0
source

All Articles