- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在Android中的表单上动态创建TEdit
:
edit := TEdit.Create(Self);
edit.Free
释放它,但是它仍然在表单上。
最佳答案
更新为10.4
Delphi 10.4 Sydney跨所有平台统一了内存管理,并删除了ARC编译器。换句话说,现在所有平台都遵循与Windows平台相同的内存管理规则。
经典(非ARC)编译器中的DisposeOf
与Free
经典编译器上的
DisposeOf
调用Free
并在功能上表现相同的DisposeOf
仅用于向后兼容,在首选使用Free
的新代码(不必与ARC编译器保持兼容性)中,首选DisposeOf
中没有被更改为Free
TComponent
后代对象时,应遵循两个规则:
DisposeOf
的
DisposeOf
后不久引用未超出范围的情况下,对象引用也应设置为nil
(在陷阱中进行详细说明)DisposeOfAndNil
方法可能很有吸引力,但是ARC使它比旧的
FreeAndNil
方法复杂得多,我建议使用纯
DisposeOf - nil
序列来避免其他问题:
Component.DisposeOf;
Component := nil;
尽管在许多情况下,即使不遵守上述规则,代码也可以正常运行,但此类代码非常脆弱,很容易被看似无关的地方引入的其他代码破坏。
DisposeOf
中断ARC。它违反了ARC
的黄金法则。任何对象引用都可以是有效的对象引用,也可以是nil ,并且引入了第三种状态-
放置了“僵尸” 对象引用。
DisposeOf
,就像只是解决Delphi特定框架问题的加法,而不是真正属于ARC本身的概念。
TComponent
类(及其所有后代)在设计时考虑了手动内存管理。它使用与ARC内存管理不兼容的通知机制,因为它依赖于破坏析构函数中的强引用周期。由于
TComponent
是Delphi框架所依赖的基本类之一,因此它必须能够在ARC内存管理下正常运行。
Free Notification
机制之外,Delphi框架中还有其他类似的设计适用于手动内存管理,因为它们依赖于破坏析构函数中的强引用周期,但是这些设计不适合ARC。
DisposeOf
方法可以直接调用对象析构函数,并使这些旧代码可以与ARC一起播放。
TComponent
的任何代码也会自动成为旧代码。
So what else does DisoseOf solve? It is very common among variousDelphi frameworks (VCL and FireMonkey included), to place activenotification or list management code within the constructor anddestructor of a class. The Owner/Owned model of TComponent is a keyexample of such a design. In this case, the existing componentframework design relies on many activities other than simple “resourcemanagement” to happen in the destructor.
TComponent.Notification() is a key example of such a thing. In thiscase, the proper way to “dispose” a component, is to use DisposeOf. ATComponent derivative isn’t usually a transient instance, rather it isa longer-lived object which is also surrounded by a whole system ofother component instances that make up things such as forms, framesand datamodules. In this instance, use DisposeOf is appropriate.
DisposeOf
时到底发生了什么,有必要知道Delphi对象销毁过程是如何工作的。
destructor Destroy
方法链Component.Free
->立即执行阶段
1 -> 2 -> 3
使用ARC编译器释放对象
Component.Free
或Component := nil
->减少对象引用计数,后跟 a)或 b)1 -> 2 -> 3
Component.DisposeOf
->立即执行1
阶段,当对象引用计数达到0时,稍后将执行2
和3
阶段。DisposeOf
不会减少调用引用的引用计数。TComponent
Free Notification
机制通知注册的组件特定的组件实例正在释放。被通知的组件可以在虚拟
Notification
方法中处理该通知,并确保清除了它们可能在销毁的组件上保留的所有引用。
Free Notification
析构函数中触发了
TComponent
机制,并且没有
DisposeOf
和析构函数的直接执行,两个组件可以相互保持强引用,从而在整个应用程序生存期内保持自身的生命。
FFreeNotifies
列表被声明为
FFreeNotifies: TList<TComponent>
,它将存储对任何已注册组件的强引用。
TEdit
和
TPopupMenu
并将该弹出菜单分配给edit的
PopupMenu
属性,则edit将在其
FEditPopupMenu
字段中强烈引用弹出菜单,而弹出菜单将在其
FFreeNotifies
列表中保留强烈引用以进行编辑。如果要释放这两个组件中的任何一个,则必须在它们上调用
DisposeOf
,否则它们将继续存在。
Menu.Free
替换为
Menu.DisposeOf
,则会触发
Free Notification
机制并破坏强引用周期。
procedure ComponentLeak;
var
Edit: TEdit;
Menu: TPopupMenu;
begin
Edit := TEdit.Create(nil);
Menu := TPopupMenu.Create(nil);
Edit.PopupMenu := Menu; // creating strong reference cycle
Menu.Free; // Menu will not be released because Edit holds strong reference to it
Edit.Free; // Edit will not be released because Menu holds strong reference to it
end;
DisposeOf的陷阱
DisposeOf
的实现方式,这还有两个主要问题。
DisposeOf
不会在调用引用
QP report RSP-14681时减少引用计数
type
TFoo = class(TObject)
public
a: TObject;
end;
var
foo: TFoo;
b: TObject;
procedure DoDispose;
var
n: integer;
begin
b := TObject.Create;
foo := TFoo.Create;
foo.a := b;
foo.DisposeOf;
n := b.RefCount; // foo is still alive at this point, also keeping b.RefCount at 2 instead of 1
end;
procedure DoFree;
var
n: integer;
begin
b := TObject.Create;
foo := TFoo.Create;
foo.a := b;
foo.Free;
n := b.RefCount; // b.RefCount is 1 here, as expected
end;
2. DisposeOf
不会清理实例内部托管类型引用
QP report RSP-14682
type
TFoo = class(TObject)
public
s: string;
d: array of byte;
o: TObject;
end;
var
foo1, foo2: TFoo;
procedure DoSomething;
var
s: string;
begin
foo1 := TFoo.Create;
foo1.s := 'test';
SetLength(foo1.d, 1);
foo1.d[0] := 100;
foo1.o := TObject.Create;
foo2 := foo1;
foo1.DisposeOf;
foo1 := nil;
s := IntToStr(foo2.o.RefCount) + ' ' + foo2.s + ' ' + IntToStr(foo2.d[0]);
// output: 1 test 100 - all inner managed references are still alive here,
// and will live until foo2 goes out of scope
end;
解决方法
destructor TFoo.Destroy;
begin
s := '';
d := nil;
o := nil;
inherited;
end;
上述问题的综合影响可能以不同的方式体现出来。从保留不必要的分配内存到难以捕获由所包含的非拥有对象和接口(interface)引用的错误,意外引用计数引起的错误。
DisposeOf
不会减少调用引用的引用计数,因此重要的是,在析构函数中对此类引用进行
nil
的引用,否则整个对象层次结构的存活时间可能比所需时间更长,有时甚至在整个应用程序生命周期中都可以存活。
DisposeOf
解析所有循环引用
DisposeOf
的问题是,只有在析构函数中有可解析循环引用的代码时,它才会中断循环引用-就像
TComponent
通知系统所做的那样。
[weak]
和/或
[unsafe]
属性来破坏不由析构函数处理的此类循环。这也是ARC的首选做法。
DisposeOf
用作打破所有引用周期(从未设计过的引用周期)的快速解决方案,因为它不起作用,滥用它可能导致难以跟踪的内存泄漏。
DisposeOf
不会破坏的循环的简单示例是:
type
TChild = class;
TParent = class(TObject)
public
var Child: TChild;
end;
TChild = class(TObject)
public
var Parent: TParent;
constructor Create(AParent: TParent);
end;
constructor TChild.Create(AParent: TParent);
begin
inherited Create;
Parent := AParent;
end;
var
p: TParent;
begin
p := TParent.Create;
p.Child := TChild.Create(p);
p.DisposeOf;
p := nil;
end;
上面的代码将泄漏子对象实例和父对象实例。结合
DisposeOf
无法清除内部托管类型(包括字符串)的事实,这些泄漏可能会很大,具体取决于您存储在内部的数据类型。打破这种循环的唯一(正确)方法是更改
TChild
类声明:
TChild = class(TObject)
public
[weak] var Parent: TParent;
constructor Create(AParent: TParent);
end;
关于android - 如何在Android/iOS中释放组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27818697/
我是一名优秀的程序员,十分优秀!