在StackOverflow上阅读了很多关于使用Interfaces自动引用计数的缺点后,我开始尝试手动引用计数每个接口实例化.
在尝试了整整一个下午后,我放弃了!
当我调用FreeAndNil(p)时,为什么会出现访问冲突?
以下是我的简单单元的完整列表.
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.
解决方法
发生访问冲突是因为FreeAndNil接收到一个非类型化的var参数,该参数应该是一个对象引用.您正在传递不符合要求的接口引用.不幸的是,你只能在运行时找到它.在我看来,这是反对使用FreeAndNil的最强点.
您的引用计数通过接口引用计数机制禁用生命周期管理.为了销毁一个对象,你需要调用它的析构函数.为了做到这一点,你必须有权访问析构函数.您的接口不公开析构函数(它不应该).因此,我们可以推断出,为了销毁对象,您需要有一个对象引用.
以下是一些选项:
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;
或者你可以这样做
var intf: IPersona; .... intf := TPersona.Create('Fabio'); try //do stuff with intf finally (intf as TObject).Free; end;