接口小论

完成一个功能时用到观察者模式,将业务类的变化通知给外部的窗体,同时也通知给另一个类,假设为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的接口真是太不直观了。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值