使用tpagecontrol时的所有权原则 [英] Ownership principles when using tpagecontrol
问题描述
两天前,我对此问题(这是让我最困扰的地方):
Two days ago I gave an accepted answer for this question (that is what bothering me the most):
newtabsheet:=ttabsheet.Create(PageControl1);
NewTabSheet.PageControl := PageControl1;
newtabsheet.Caption:='tab1';
i1:=tabs.Count;
tabs.Add(newtabsheet);
我已经尽力分析了这段代码,这就是我的结果.
I have analysed this code the best I can, and these are my results.
第1行:我创建了一个ttabsheet
,其中pagecontrol1
作为父级/所有者(基于下面的构造函数).
Line 1: I create a ttabsheet
with pagecontrol1
as the parent/owner (based on the constructor below).
constructor TTabSheet.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Align := alClient;
ControlStyle := ControlStyle + [csAcceptsControls, csNoDesignVisible,
csParentBackground, csPannable];
Visible := False;
FTabVisible := True;
FHighlighted := False;
end;
然后我将对它的引用存储在变量Newtabsheet
中(此建议基于@David Heffernan给
Then I stored a reference to it in the variable Newtabsheet
(this remarque is based on an answer @David Heffernan gave to one of my questions).
第2行:我使用引用将pagecontrol1
分配给属性.pagecontrol
.
Line 2: I used the reference to assign pagecontrol1
to the property .pagecontrol
.
这是设置方法的代码:
procedure TTabSheet.SetPageControl(APageControl: TPageControl);
begin
if FPageControl <> APageControl then
begin
if FPageControl <> nil then
FPageControl.RemovePage(Self);
Parent := APageControl;
if APageControl <> nil then
APageControl.InsertPage(Self);
end;
end;
基于此,我认为(可能是错误的)它正在将现有父级与新传递的参数进行比较,如果存在差异,则parent<>nil
从先前的pagecontrol
中删除此newtabsheet
(视觉影响)并将其分配给新的父级/所有者,然后将新的parent<>nil
插入到新的pagecontrol
(视觉效果)中.
Based on this I think (maybe wrong) it is comparing the existing parent to the new passed parameter, if there is a different then if the parent<>nil
removes this newtabsheet
from the previous pagecontrol
(visual affects) and assign it to the new parent/owner, then if the new parent<>nil
insert it in the new pagecontrol
(visual affects).
所以这一行是不必要的(可能是错误的),因为我已经在第一行中做了.
So this line is unnecessary (maybe wrong), because I already did that in the first line.
第3行:我使用引用为tabsheet
的标题分配了'tab1'.
Line 3: I use the reference to assign the caption of the tabsheet
with 'tab1'.
第5行:我使用add方法将对tabsheet
的引用存储在选项卡列表tabs:TList<ttabsheet>;
通用中.
Line 5: I use the add method to store a reference to the tabsheet
in the tabs list tabs:TList<ttabsheet>;
a generic.
这是我头顶飞舞的地方.
Here is where things start to fly over my head.
我认为父母/所有者是tabs
,但是pagecontrol1
仍在显示选项卡(基于对此
I think that the parent/owner is tabs
, but the pagecontrol1
is still showing the tab (based on the accepted answer to this question changing the parent visually removes the tabsheet
from pagecontrol1
), but it does not.
现在这可能是错误的,但是如果它只是一个参考,那为什么当我通过执行PageControl1.ActivePage.free
tabs.count
从pagecontrol1
中删除tabsheet
时仍保持不变.
Now this could be wrong, but again if it is just a reference then why when I delete the tabsheet
from pagecontrol1
by doing PageControl1.ActivePage.free
the tabs.count
remains constant.
如果我从选项卡中删除tabsheet
,则pagecontrol1
上的tabsheet
不会被删除(以可视方式删除).
And if I delete the tabsheet
from tabs then the tabsheet
on the pagecontrol1
is not deleted (removed visually).
从此问题我知道,泛型成为了父级/所有者,您不必担心从pagecontrol1
中释放tabsheet
,因为制表符是父级,您只需要从tabs
中释放它即可.
From this question I understood that generics becomes the parent/owner and that you do not need to worry about freeing tabsheet
from pagecontrol1
, because tabs is the parent and you only need to free it from tabs
.
我的问题:这段代码中发生了什么以及为什么我会遇到这种情况?
My question: What is happening in this code and why I'm facing this behavior?
引起问题的是,当我删除pagecontrol中的ttabsheet时,为什么当我尝试使用列表中的ttabsheet是两个不同的对象时,它没有引发错误.
What is driving the question is when I delete the ttabsheet in the pagecontrol why it is not raising an error when I try to use the reference to it in the list are they two different objects.
推荐答案
Owner
负责内存管理.将TPageControl
分配为TTabSheet
的Owner
意味着TPageControl
将在TPageControl
被销毁时销毁TTabSheet
. Owner
和所有者拥有彼此指向的指针,因此它们可以相互通知与内存管理有关的重要事件.
The Owner
is responsible for memory management. Assigning the TPageControl
as the Owner
of the TTabSheet
means the TPageControl
will destroy the TTabSheet
when the TPageControl
is destroyed. The Owner
and ownee contain pointers to each other so they can notify each other of important events related to memory management.
Parent
负责窗口管理和视觉呈现.将TPageControl
分配为TTabSheet
的Parent
表示TTabSheet
的窗口是TPageControl
窗口的子级,并将显示在TPageControl
的客户区中窗户. Parent
和child包含彼此的指针,因此它们可以相互通知与窗口管理相关的重要事件.
The Parent
is responsible for window management and visual presentation. Assigning the TPageControl
as the Parent
of the TTabSheet
means the TTabSheet
's window is a child of the TPageControl
's window, and will be displayed inside the client area of the TPageControl
's window. The Parent
and child contain pointers to each other so they can notify each other of important events related to window management.
Owner
和Parent
是两种不同的东西.它们可以是同一对象,但不必相同(例如-设计时创建的组件始终由TForm
,TFrame
或TDataModule
拥有)正在设计中,但可以是容器组件(例如TPanel
等)的子代.
The Owner
and Parent
are two different things. They can be the same object, but they don't have to be (case in point - components created at design-time are always owned by the TForm
, TFrame
, or TDataModule
that is being designed, but can be children of container components, like TPanel
, etc).
TList
不承担任何责任.它只是一个任意值的动态数组,在这种情况下,它们恰好是TTabSheet
指针.而已. TList
和TTabSheet
之间根本没有所有者/父母关系.
The TList
is responsible for nothing. It is just a dynamic array of arbitrary values, which in this case happen to be TTabSheet
pointers. Nothing more. There is no Owner/Parent relationship between the TList
and TTabSheet
at all.
TTabSheet
被销毁时,其自身与TPageControl
之间存在关系链接. TTabSheet
将从其TPageControl
的管理中删除.它不再由TPageControl
拥有,并且不再是TPageControl
的子级.这两个对象彼此清除了指针.
When the TTabSheet
is destroyed, there are relational links between itself and its TPageControl
. The TTabSheet
removes itself from its TPageControl
's management. It is no longer owned by the TPageControl
, and is no longer a child of the TPageControl
. The two objects clear their pointers to each other.
TTabSheet
和TList
之间根本没有关系链接. TTabSheet
甚至不存在TList
的概念.将TTabSheet
添加到TList
和从其中删除时,TList
只是在添加指向TTabSheet
对象的指针. TList
不会将插入/移除通知TTabSheet
.而且,没有从TTabSheet
到TList
的指针,因此当TTabSheet
被销毁时,没有适当的机制允许从TList
删除指向该TTabSheet
的指针.指针仍保留在列表中,只要不取消引用指针即可访问已破坏的TTabSheet
.
There is no relational link between the TTabSheet
and the TList
at all. The TTabSheet
has no concept that the TList
even exists. When the TTabSheet
is added to and removed from the TList
, the TList
is simply adding/removing a pointer to the TTabSheet
object. The TList
does not notify the TTabSheet
about the insertion/removal. And there is no pointer from the TTabSheet
to the TList
, so when the TTabSheet
is destroyed, there is no mechanism in place that allows the pointer to that TTabSheet
to be removed from the TList
. The pointer remains in the list, and you can continue using the pointer (for comparisons, etc) as long as you do not dereference it to access the destroyed TTabSheet
.
如果要在TTabSheet
被销毁时自动从TList
中删除TTabSheet
指针,则需要调用TTabSheet
的FreeNotification()
方法.然后,您的Notification()
回调可以从TList
中删除TTabSheet
指针,以响应opRemove
通知,例如:
If you want to remove the TTabSheet
pointer from the TList
automatically when the TTabSheet
is destroyed, you need to call the TTabSheet
's FreeNotification()
method. Your Notification()
callback can then remove the TTabSheet
pointer from the TList
in response to opRemove
notifications, eg:
TMyForm = class(TForm)
...
private
tabs: TList<TTabSheet>;
...
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override; // <-- ADD THIS
...
end;
...
procedure TMyForm.DoSomething;
var
NewTabSheet: TTabSheet;
...
begin
...
NewTabSheet := TTabSheet.Create(PageControl1);
NewTabSheet.PageControl := PageControl1;
NewTabSheet.Caption := 'tab1';
tabs.Add(NewTabSheet);
NewTabSheet.FreeNotification(Self); // <-- ADD THIS
...
end;
procedure TMyForm.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent is TTabSheet) then
tabs.Remove(TTabSheet(AComponent)); // <-- HERE
end;
如果要在从TList
中删除TTabSheet
的指针时自动从TPageControl
中删除TTabSheet
,请切换到TObjectList
.它的OwnsObjects
属性默认情况下为true,因此当使用Remove()
或Delete()
方法从列表中删除指针时,它将破坏TTabSheet
. Extract()
方法不会破坏TTabSheet
,因此请在Notification()
回调中使用它代替Remove()
,例如:
If you want the TTabSheet
to be removed from its TPageControl
automatically when its pointer is removed from the TList
, switch to TObjectList
instead. Its OwnsObjects
property is true by default, so it will destroy the TTabSheet
when the pointer is removed from the list using the Remove()
or Delete()
method. The Extract()
method will not destroy the TTabSheet
, so use that instead of Remove()
in your Notification()
callback, eg:
TMyForm = class(TForm)
...
private
tabs: TObjectList<TTabSheet>;
...
end;
...
procedure TMyForm.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (Operation = opRemove) and (AComponent is TTabSheet) then
tabs.Extract(TTabSheet(AComponent)); // <-- HERE
end;
这篇关于使用tpagecontrol时的所有权原则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!