低级问题,动态创建panel 释放时 是否需要释放panel上动态创建的东西?
比如 我动态创建一个panel 在这个panel上再次创建一个按钮 和图片等, 点击按钮释放 panel
就是不知道在释放panel的释放是否需要释放容器内的控件,包括按钮本身
大概代码
- Delphi(Pascal) code
procedure TForm1.Button1Click(Sender: TObject); var t:TPanel; b:TButton; img:TImage; begin t:=TPanel.Create(Self); //创建panel t.Parent:=Self; t.Width:=300; t.Height:=50; t.Top:=0; t.Left:=0; b:=TButton.Create(t); //在panel上创建一个按钮 b.Parent:=t; b.Left:=0; b.Top:=0; img:=TImage.Create(Self); // 在panel上创建一个img img.Parent:=t; img.Left:=30; img.Top:=0; img.Picture.LoadFromFile('c:\1.bmp'); b.OnClick:=Button2.OnClick; //释放事件end;procedure TForm1.Button2Click(Sender: TObject);var b:TButton;begin b:=TButton(Sender); //这里是否要先释放父控件上的动态创建的东西???????? b.Parent.Free; //释放父控件,也就是创建的panel end;[解决办法]
不好意思,可能是我的鼠标有问题,回了两次了,抱歉。
其实,上述的问题,我们可以来个测试:
- Delphi(Pascal) code
procedure TForm1.Button1Click(Sender: TObject);var Panel:TPanel; Button:TButton;begin Panel:=TPanel.Create(self); Panel.Parent:=Form1; Button:=TButton.Create(Panel); Button.Parent:=Panel; showmessage('动态建立了Panel和Button'); Panel.Free; if Assigned(Panel) then showmessage('Panel释放了,但它的指针还在'); try Button.Free; //如这句能不出错,证明它还在 showmessage('Panel释放了,但Button还没释放。'); except showmessage('Button已经不存在.'); end; Panel:=TPanel.Create(self); Panel.Parent:=Form1; Button:=TButton.Create(Panel); Button.Parent:=Panel; showmessage('再次动态建立了Panel和Button'); FreeAndNil(Panel); if not Assigned(Panel) then showmessage('Panel释放了,它的指针也同时释放了'); try Button.Free; showmessage('Panel释放了,但Button还没释放。'); except showmessage('Button已经不存在.'); end; if Assigned(Button) then showmessage('Button虽然不在了,但它的指针还在') else showmessage('Button不在了,它的指针也不存在'); Button:=nil; if Assigned(Button) then showmessage('Button的指针还在') else showmessage('Button的指针不在了');end;
[解决办法]
var Panel:TPanel;
Button:TButton;
begin
Panel:=TPanel.Create(self);
Panel.Parent:=Form1;
Button:=TButton.Create(self);
Button.Parent=Panel;
Panel.Free;
end;
只要释放Panel; Button对象也跟着会释放;也就是 如果一个控件有父对象 当父释放时候 子也必然会释放。
一下是VCL 源码:
设置父对象时候 调用的 一段源码
procedure TWinControl.Insert(AControl: TControl);
begin
if AControl <> nil then
begin
if AControl is TWinControl then
begin
ListAdd(FWinControls, AControl);
ListAdd(FTabList, AControl);
end else
ListAdd(FControls, AControl);
AControl.FParent := Self;
end;
end;
父控件释放的时候的 源码
destructor TWinControl.Destroy;
var
I: Integer;
Instance: TControl;
begin
Destroying;
if FDockSite then
begin
FDockSite := False;
RegisterDockSite(Self, False);
end;
FDockManager := nil;
FDockClients.Free;
if Parent <> nil then RemoveFocus(True);
if FHandle <> 0 then DestroyWindowHandle;
I := ControlCount;
while I <> 0 do
begin
Instance := Controls[I - 1];
Remove(Instance);
Instance.Destroy;
I := ControlCount;
end;
FBrush.Free;
{$IFDEF LINUX}
if FObjectInstance <> nil then WinUtils.FreeObjectInstance(FObjectInstance);
{$ENDIF}
{$IFDEF MSWINDOWS}
if FObjectInstance <> nil then Classes.FreeObjectInstance(FObjectInstance);
{$ENDIF}
inherited Destroy;
end;
function TWinControl.GetControlCount: Integer;
begin
Result := 0;
if FControls <> nil then Inc(Result, FControls.Count);
if FWinControls <> nil then Inc(Result, FWinControls.Count);
end;
[解决办法]
这里有两个属性需要注意
1:Owner
对于所有从TComponent继承下来的对象都有Owner属性,该属性值是作为构造函数的参数Create(Owner: TComponent)构造时被赋值。Owner指的是容器对象,当被添加到容器中后,容器释放前,会自动释放中间所有的成员。也就是说,当一个对象在构造时写成Create(Owner),则Owner被释放时,会自动释放该对象,成员对象不需要手工释放。
对象在构造时可以不指定容器对象,写成Create(nil),则该对象需要注意手工释放。
如果成员对象在容器之前释放,例如,Item= TItem.Create(Owner);Item在Owner之前被释放,容器Owner随后释放时,是不会出错的,因为成员Item在释放的时候,会在容器Owner列表中搜索自己,搜索到自己后从列表中移除。
如果需要在对象构造之后改变其容器Owner,使用容器Owner的RemoveComponent从Owner中移除自己,调用新容器的InsertComponent,将自己加入到新容器。
2:Parent
该属性虽然被定义在TControl中,但是在TWinControl中被使用。该属性的本意是用来控制UI控件在显示时是属于哪个控件之内,也就是所谓的控件容器control container。注意,这里和上面提到的Owner并不相同,Parent不负责释放其包含的控件对象。
Parent属性可以直接被取值赋值,从而改变控件的包含关系。注意,当Parent发生改变后,控件位置属性的坐标也会改变为以父控件的相对坐标,即子控件的Top Left 的起始点是Parent的左上角,改变Owner不会影响到该属性的值。
楼主贴出的代码中,如果你需要判定一个对象变量的是否被释放的话,建议你在释放的时候写成FreeAndNil(Obj),而不是Obj.Free,这样的好处是你可以使用Obj=nil这样的语句来判定Obj是否被释放。
函数Assigned()一般是用来判定函数指针(函数变量)是否是空指针,当用它来判定对象时,等同于Obj=nill,如果Obj不是使用FreeAndNil释放的时候,Assigned()也可能判定不正确。