完成一个功能时用到观察者模式,将业务类的变化通知给外部的窗体,同时也通知给另一个类,假设为TTest,这个类继承自TInterfaceObject。窗体和TTest都实现了一个接口,因此业务类通过接口可以将变化通知给外部。
感觉这个类层次应用得很好,但问题出现了,业务类用IInterfaceList管理这些接口,程序关闭时,IInterfaceList将所有的接口置为Nil,按以前的理解,实现接口的类对象会同时被消毁。即TTest对象和窗体类对象自动会被消毁。但事实上并不是这样。
经过一番研究,才明白个中原因,有结论如下:
1.类如果从TInterfaceObject继承下来,并且实现某个接口。
该接口变量可以自动释放实现该接口的类,例子如下:
type
ITest = interface(IInterface)
procedure test;
end;
TTest = class(TInterfacedObject, ITest)
public
procedure test;
destructor Destroy; override;
end;
{TTest}
procedure TTest.test;
begin
showmessage(‘ok‘);
end;
destructor Destroy;
begin
showmessage(‘ok‘);
inherited;
end;
//测试
var
Test: ITest;
begin
Test := TTest.Create;
try
Test.test;
finally
Test := nil;
end;
end;
执行后,弹出两次对话框,说明TTest对象最后被释放了,原因是Test := nil 后,最终于会调用TInterfaceObject的_Release方法,在里面如果引用计数为0,则调用Destroy释放自己。
2. 类如果从TComponent继承下来,并且实现某个接口。
该接口变量不能自动释放实现该接口的类,把上面的TTest改为从TComponent继承,测试代码如下:
var
Test: ITest;
begin
Test := TTest.Create(nil);
try
Test.test;
finally
Test := nil;
end;
end;
这时只弹出一个对话框,说明对象没有被释放,原因是Test := nil后,最终于会调用TComponent的_Release,而里面如果引用计数为0并不会释放自己。
3.要对象可以由接口自动释放,必须该类自己实现IInterface,并在_Release调用Destroy。例子如下:
type
ITest = interface(IInterface)
procedure test;
end;
TTest = class(TComponent, IInterface, ITest)
public
{ IInterface }
function QueryInterface(const IID: TGUID;
out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
{ ITest }
procedure test;
destructor Destroy; override;
end;
...
{ TTest }
destructor TTest.Destroy;
begin
ShowMessage(‘ok‘);
inherited;
end;
function TTest.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
procedure TTest.test;
begin
ShowMessage(‘ok‘);
end;
function TTest._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TTest._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then
Destroy;
end;
//测试代码
var
Test: ITest;
begin
Test := TTest.Create;
try
Test.test;
finally
Test := nil;
end;
end;
运行结果,对话框弹了两次,对象可以由接口释放了。
Delphi的接口真是太不直观了。
原文地址:https://www.cnblogs.com/h2zZhou/p/9208303.html