一个接口的自动释放问题 ( 积分: 100 )

  • 主题发起人 主题发起人 chenglh
  • 开始时间 开始时间
C

chenglh

Unregistered / Unconfirmed
GUEST, unregistred user!
以下代码出错,请问是为什么呢?谢谢!

IDbDealer是一个接口定义,有GUID的接口。
TDmDBDealer是一个TDataModule,实现了IDbDealer接口。
函数代码如下:
var
dmDBDealer:TDmDBDealer;
begin
dmDBDealer:=TDmDBDealer.Create(nil);
dmDBDealer.ConnectionStr:=untCommon.ComposeADOConnnectionStr('.','dbFG','sa','');
sql:=format('select * from %s',[TABLE_NAME_DICT]);
(dmDBDealer as IDmDbDealer).OpenCDS(sql,cdsFile);
dmDBDealer.Free;
end;
调试过程中一执行到函数结尾就报错。

改成下面的代码就不出错了。
var
dmDBDealer:TDmDBDealer;
aIDBDealer:IDbDealer;
begin
dmDBDealer:=TDmDBDealer.Create(nil);
dmDBDealer.ConnectionStr:=untCommon.ComposeADOConnnectionStr('.','dbFG','sa','');
aIDBDealer:=dmDBDealer;
sql:=format('select * from %s',[TABLE_NAME_DICT]);
aIDBDealer.OpenCDS(sql,cdsFile);
aIDBDealer:=nil;
dmDBDealer.Free;
end;

我想应该与接口的自动释放有关系,可是我不明白
显式转换和临时变量的区别怎么这么大呢?隐式转换接口为什么会抱错?请高人指点,谢谢了
 
以下代码出错,请问是为什么呢?谢谢!

IDbDealer是一个接口定义,有GUID的接口。
TDmDBDealer是一个TDataModule,实现了IDbDealer接口。
函数代码如下:
var
dmDBDealer:TDmDBDealer;
begin
dmDBDealer:=TDmDBDealer.Create(nil);
dmDBDealer.ConnectionStr:=untCommon.ComposeADOConnnectionStr('.','dbFG','sa','');
sql:=format('select * from %s',[TABLE_NAME_DICT]);
(dmDBDealer as IDmDbDealer).OpenCDS(sql,cdsFile);
dmDBDealer.Free;
end;
调试过程中一执行到函数结尾就报错。

改成下面的代码就不出错了。
var
dmDBDealer:TDmDBDealer;
aIDBDealer:IDbDealer;
begin
dmDBDealer:=TDmDBDealer.Create(nil);
dmDBDealer.ConnectionStr:=untCommon.ComposeADOConnnectionStr('.','dbFG','sa','');
aIDBDealer:=dmDBDealer;
sql:=format('select * from %s',[TABLE_NAME_DICT]);
aIDBDealer.OpenCDS(sql,cdsFile);
aIDBDealer:=nil;
dmDBDealer.Free;
end;

我想应该与接口的自动释放有关系,可是我不明白
显式转换和临时变量的区别怎么这么大呢?隐式转换接口为什么会抱错?请高人指点,谢谢了
 
(dmDBDealer as IDmDbDealer).OpenCDS(sql,cdsFile);
这是做什么?
你直接
dmDBDealer.OpenCDS(sql,cdsFile);
不可以吗?
 
dmDBDealer:TDmDBDealer;应该是个生命周期托管的对象,也就是说要么是交给宿主来管理,要么就是自己创建自己释放,
aIDBDealer:IDbDealer;是个COM接口,生命周期是COM环境决定的
dmDBDealer as IDmDbDealer这句话是导致错误的吧..
偶以为(未看源代码)在此操作中,已经将对象转换为接口,生命周期已经有接口来管理,不用自己释放了.
但是在最后,dmDBDealer.Free;释放操作将对象释放,同时将接口=nil,但并没有将接口计数清零.导致程序离开时,环境再次释放接口指针,从而引发错误.
 
To zbird:
OpenCDS是TDmDBDealer的私有函数,变通的方法肯定性,我只是想知道怎么回事。
To dinglj1760:
我调试跟踪了下,如果不调用dmDbDealer.Free,光靠接口的自动释放, 没有发现调用TDmDBDealer.onDestroy函数呢
 
如果OpenCDS是私有函数,那这本来就已经违背了接口使用的本来目的。
好象接口释放的时候调用的就是free方法。
如果对接口自动释放不放心的话可以用内存检测工具查看一下是否存在内存泄漏问题。
 
dmDBDealer as IDmDbDealer 你的类实现几个接口啊,要是一个就没必要了。
destructor Destroy
override;自动释放啊
 
TdmDBDealer只实现一个接口。我觉得显式转换和临时变量是一回事啊
为什么显式转换报错呢?
自动释放也是在引用计数为0时才Free啊。上面的代码显式转换后引用计数应该为1啊(dmDBDealer指向它),不会自动释放的。TdmDBDealer的父类TDataModule应该自己写好的_AddRef函数吧。
我记得以前用过显式转换(TXX as IXX)的方法,然后TXX.Free,不出错。不知道这一次是怎么回事,糊涂。
 
跟踪一下,看看哪里出的问题,贴出来看看
 
唉。。。又是基本常识的问题。。。

想清楚了临时变量的生存期这个问题就迎刃而解了:当执行到end;处的时候Delphi会帮你自动释放由(dmDBDealer as IDmDbDealer)生成的临时变量,而在此之前你已经使用dmDBDealer.Free;将对象都释放了,岂有不出错之理?
 
To godelphi2004:
谢谢,好像确实是这个原因。我本来以为(dmDBDealer as IDmDbDealer)生成的临时变量在调用这个语句后立即释放,估计实际情况是在end后释放的,这样就把顺序(:=nil 和 Free)给颠倒了,这样肯定会出错。

但是我以前的代码,情况差不多,却不出错。
procedure TfmArchive.actViewExecute(Sender: TObject);
var
ArchiveID:integer;
begin
if activeControl=tvwCategory then
begin
actViewCategoryExecute(nil);
exit;
end
else
begin
if not assigned(lvwArchive.Selected) then
exit;
ArchiveID:=integer(lvwArchive.Selected.Data);
fmArchiveInfo:=TfmArchiveInfo.create(self);
(fmArchiveInfo as IItemDetailUI).LoadRecord(IntToStr(ArchiveID));
(fmArchiveInfo as IItemDetailUI).View;
if (fmArchiveInfo as IItemDetailUI).DataSaved then
begin
lvwArchive.Selected.Caption:=fmArchiveInfo.cdsItem.fieldbyName('Name').asstring;
lvwArchive.Selected.subItems[0]:=fmArchiveInfo.cdsItem.fieldbyName('Code').asstring;
//lvwArchive.Selected.subItems[1]:=fmArchiveInfo.cdsItem.fieldbyName('Indate').asstring;
lvwArchive.Selected.subItems[2]:=fmArchiveInfo.cdsItem.fieldbyName('Remark').asstring;
end;
fmArchiveInfo.Free;
end;
end;

上面这段代码中,IItemDetailUI接口也有个GUID,以前没有这个GUID时这样写就出错,加了GUID就不出错了。fmArchiveInfo实现这个接口是通过属性的Implements关键字实现的。那个属性还继承自一个父类TItemDetailUI_Default。

按理说这段代码应该会出一样的错误,为什么有不出错呢?再谢
 
建议楼主看看delphi一些基本的书,
推荐看李维的《Inside VCL》一书。
看看delphi里面接口的历史渊源。
delphi的接口有原生接口和COM接口,前者不带GUID,后者带。
所以生命周期也就不同了,请再次看看我最初的留言。
 
手头没有这本书。Com接口和非com接口的生命周期区别在哪里呢?
 
谢谢大家!
今天去查了资料,总算搞清楚了点。在一本书的第一章里就有这么一段话,还是黑体:
“对象模型和接口模型不要混合”“注意,当用户(程序员)感到必须将对象模型和接口模型混合,那么应用程序中可能存在一个设计上的错误。”,并称这个错误是个“非常经典”的错误。很高兴,偶犯的错误也是经典的。(其实也可以翻译成“非常典型”的错误)
dinglj1760说的全对。我还是觉得Delphi对接口的支持在这点上不够直观,帮助里也没有明确警告这个“典型的”错误。实际上一旦使用了Txx as Ixx语句,这个错误几乎在所难免,除非显式地调用_Addref,_Release函数方可避免。
上面的代码,需要修改接口定义了,将ConnectionStr作为一个属性加到接口定义中去,其实早在定义这个接口时就考虑要不要加这个属性,最后莫名其妙地决定不加,走了许多弯路,还是要加。
谢谢大家。
 
接受答案了
 
感谢大家的参与,这几天恶补接口方面的东西(object Pascal),做了笔记,欢迎大家访问我的接口学习笔记,希望得到批评和指正。
 
后退
顶部