控件的Destroy出错问题:无法释放相关的对像 (50分)

  • 主题发起人 主题发起人 南腔北调
  • 开始时间 开始时间

南腔北调

Unregistered / Unconfirmed
GUEST, unregistred user!
unit CHOPCombobox;

interface

uses
SysUtils, Classes, Controls, StdCtrls,Forms,messages,CHManageDBConn,shareproc,
CustomDataClass;

type
TComboBoxTest = class(TComboBox)
private
{ Private declarations }
protected
{ Protected declarations }
public
procedure CreateWnd;override;
destructor Destroy; override;
{ Public declarations }
published
{ Published declarations }
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('CHGroupware', [TComboBoxTest]);
end;

{ TComboBoxTest }

procedure TComboBoxTest.CreateWnd;
var cid:tid;
begin
inherited;
if not (csDesigning in ComponentState) then
begin
cid:=tid.Create;
cid.id:=1;
items.AddObject('bbbbbbbb',cid);
cid:=tid.Create;
cid.id:=2;
items.AddObject('aaaaaaa',cid);
cid:=tid.Create;
cid.id:=3;
items.AddObject('ccccc',cid);
end;
end;

destructor TComboBoxTest.Destroy;
var
i:integer;
ftid:tid;
begin
if not (csDesigning in ComponentState) then
begin
for i:=0 to items.Count-1 do
begin
if items.Objects<>nil then
if items.Objects is tid then
begin
ftid:=(items.Objects as tid);
items.Objects:=nil;
ftid.Free;
end;
end;
end;
inherited;
end;
 
TID的定义:
TID=class(tobject)
id: integer;
ids:string;
end;
 
你的數據定義和使用很有問題
1.) var cid:tid; //你定義的是局部變量,不可能在其他部分中釋放;

2.) cid:=tid.Create;
cid.id:=1;
items.AddObject('bbbbbbbb',cid);
cid:=tid.Create;
cid.id:=2;
items.AddObject('aaaaaaa',cid);
cid:=tid.Create;
cid.id:=3;
items.AddObject('ccccc',cid);
你的cid重複創建了!!

建議你用全局變量定義為一個數組,如 array[0...10] of tid
 
to kouchun
您对对像的引用机制理解有问题吧?
cid只是存储了tid.create创建对像的引用,并不是对像本身!

这段代码:
for i:=0 to items.Count-1 do
begin
if items.Objects<>nil then
if items.Objects is tid then
begin
ftid:=(items.Objects as tid);
items.Objects:=nil;
ftid.Free;
end;
end;
你放到一个button的click事件运行就不会有问题了,只是当他放在Destroy中时才会出现错误
 
^^^^^^^^^^
??????????
 
把下面两句的次序换一下试试。
items.Objects:=nil;
ftid.Free;
 
for i:= items.Count - 1 to 0 do
....
 
没用
我加了下面一句:
if not (csDesigning in ComponentState) then
begin
showmessage('aaaaaaaaa');//根本就不会运行。。。

感觉跟控件的释放有关,在Destroy之前,控件本身应该还没释放吧?
为什么一进入Destroy就出错呢?
只要把这个控件加在某个窗体上运行,关闭窗口时会提示:
第一个:
---------------------------
Debugger Exception Notification
---------------------------
Project Project1.exe raised exception class EInvalidOperation with message 'Control 'aa' has no parent window'. Process stopped. Use Step or Run to continue.
---------------------------
OK Help
---------------------------


f8后第二个:
---------------------------
Application Error
---------------------------
Exception EInvalidOperation in module Project1.exe at 0003E4CC.

Control 'aa' has no parent window.
---------------------------
确定
---------------------------
 
to kouchun
您对对像的引用机制理解有问题吧?
cid只是存储了tid.create创建对像的引用,并不是对像本身!
//////
我同意你上述的说发。但你犯了如下几个错误。
1:)procedure TComboBoxTest.CreateWnd;
var cid:tid;
begin
inherited;
。。。。。。。
。。。。。。。
end;
此时的cid是个局部变量,它的作用域是该函数体,生命期包含整个运行期。你重栽CreateWnd对象方法,并且用你自定义单元中的Tid类定义对象,你必须在该函数体中及时释放,否者会内存泄露。你在别的对象方法中释放该对象是徒劳的。你上面连续建立了3个Tid对象,其中有两个已经没有根了,当然第三个也是一个占有内存的壳。
2:)destructor TComboBoxTest.Destroy;
你的该对象方法目的无非就是释放在上面方法中释放的3个Tid对象,如上所述,肯定出错。
其实kouchun的建议并没错。
;
 
回忆下你用过的Tstringlist 类的用法。
procedure TForm1.Button1Click(Sender: TObject);
var
stringlist:Tstringlist;
begin
try
stringlist:=Tstringlist.create;
.........
.........
finally
stringlist.free;
end;
想想你是否是这样用的,建立----》及时释放。
你在事件处理程序中可以用的很好,那为什么在你定义的类中就不注意呢?呵呵。。。。
 
to moshengren
1、根本还没运行哪里
2、你是想说改成这样吧:
for I := Items.Count - 1 downto 0 do

to hygsxy
呵呵,有些俺暂时不敢苟同,希望再讨论:

》》并且用你自定义单元中的Tid类定义对象,你必须在该函数体中及时释放,否者会内存泄露。
这就是内存泄露?内存泄露应该是指当你建立了一个内存区域,而当整个程序或某个流程完成时,还未释放的情况才是吧,显然,我是编写控件,整个控件从建立到释放就是一流程,控件未Destroy之前,我建的TID都是有用的,在这个流程中我已有负责所建tid释放的代码

》》你上面连续建立了3个Tid对象,其中有两个已经没有根了,当然第三个也是一个占有内存的壳。
怎么会没根呢?(你说的根应该就是对像引用吧)
当用addobject加入时,他的引用已经被存入对应的items.object[x]中

至于kouchun说的,可能他习惯这样的做法吧(全局变量)
不过我是希望用面向对像编程,是在编写一个控件,我希望控件他自己能管理自己创建的对像!
 
楼主的水平很高嘛,这样的用法的确比较少人用的说。

理论上是没有错,不过我觉得到调用Destroy时,有些东东可能已经被释放了,在Destroy里释放这些内容可能晚了一点。因为控件都是相互关联的,比如看你的错误提示,可能是这个控件的parent window先于这个控件被释放,某些操作就无法进行了。

解决的办法是:研究一下TComboBox的源码,了解一下它的释放过程,找到一个早一点的切入点来释放你的东东。
 
to 猛禽
您别笑话我了,俺是菜鸟一名
看看我已前问的问题就知道了。。。

我也是急用。。。希望快点解决
 
即使你的程序是对的, 这样的写法也不好.
何况你的程序是错的.
如果只是想知道为什么, 那倒是可以讨论讨论的.
 
to wlmmlw
还请您请教,哪种写法才是好的呢?
我也是一直摸索,更希望有个尝试过的前辈指导一下
还有
我程序有错,还请您指出来,好让我改正,不然自己错了,还蒙在鼓里
谢谢
 
内存泄露就是指已经存在的独立的内存,你就可以把它看做是内存泄露。没人释放,没人管理。
不好意思,你添加的实例代码我没仔细研究。
把你的代码又仔细的看了一遍,你说的也不无道理,先提出我补充看法:
1:)不要使用全局变量。
2:在procedure TComboBoxTest.CreateWnd;方法中补加3个tid:=nil;
3: begin
for i:=0 to items.Count-1 do
begin
if items.Objects<>nil then
if items.Objects is tid then
begin
(items.Objects as Tid).free;//直接释放。(如果不行,改为items.objects.free再试)。
end;
end;
end
.........................
尽快测试,我等你结果。
 
脑子乱了。纠正:是在最后加一个tid:=nil;就行了。
 
不行
我觉得您最好亲自TEST一下,理论上很多事情好像可以,实际确不行

这个暂时搁这吧
现在没时间停在这里,只能想办法绕过了
各位如果有时间的话
可以试试看能否解决
 
TO:hygsxy
2:没必要,因为这些OBJECT并非COM OBJECT,设为NIL也没用。而且如果是COM OBJECT,这样做反而错。
3:本质上与楼主原来的用法是一样的。

to wlmmlw
不知道能否指出具体的错误?
 
后退
顶部