在 Android 中释放动态创建的控件
Freeing dynamically-created controls in Android
在我的 Firemonkey 多平台应用程序中,我动态创建了一些控件。用完就销毁
在Windows中,这个动态创建和销毁的进程没有问题。但是,似乎在 Android 中,下面的代码不会破坏控件,因为会弹出一个错误提示该控件已经存在(而且当我尝试重新创建时,我确实看到旧控件仍然存在控件)。
如何确保控件在 Android 中被正确销毁(and/or iOS - 我假设它与 iOS 相似)?
for i := oParentRect.ControlsCount-1 downto 0 do
begin
oControl := oParentRect.Controls[i];
if (oControl is TText)
or (oControl is TEdit)
then
begin
FreeAndNil( oControl );
// I have also tried oControl.Free;
end;
end;
在 RAD Studio 10.4 之前,Delphi 在 iOS 和 Android 平台上使用 ARC (Automatic Reference Counting) 进行对象生命周期管理。在 ARC 下,TObject.Free()
和 FreeAndNil()
的行为与您预期的不同。它们被编译器翻译成纯粹的 nil
赋值,减少对象的引用计数。一个对象在其引用计数下降到 0 之前不会被释放。
在你的例子中,有问题的对象有多个引用(在 Controls[]
列表中,在 oControl
变量中),所以 FreeAndNil()
没有达到预期的效果因为它只是将 oControl
变量设置为 nil 而不是从 Controls[]
列表中删除对象。换句话说,您的示例实际上与以下示例相同:
for i := oParentRect.ControlsCount-1 downto 0 do
begin
oControl := oParentRect.Controls[i]; // <-- increments refcnt
if (oControl is TText) or (oControl is TEdit) then
begin
//FreeAndNil( oControl );
oControl := nil; // <-- decrements refcnt
end;
end;
如果你真的想在ARC下编码时立即销毁一个对象,你需要使用TObject.DisposeOf()
来代替,例如:
for i := oParentRect.ControlsCount-1 downto 0 do
begin
oControl := oParentRect.Controls[i]; // <-- increments refcnt
if (oControl is TText) or (oControl is TEdit) then
begin
//FreeAndNil( oControl );
oControl.DisposeOf; // <-- destroys object, but does not free its memory yet
oControl := nil; // <-- decrements refcnt
end;
end;
在这个模型下,当一个对象被“处置”时,它的析构函数会立即被调用,但它的底层内存块还没有被释放。引用计数仍然发生在块上。对象处于 "disposed" 状态,直到其引用计数降为 0,然后释放内存块。不再调用析构函数。
此行为记录在 Embarcadero 的 DocWiki 上:The Free and Dispose Of methods under ARC
现在,话虽这么说,在 RAD Studio 10.4 中,Embarcadero has removed object ARC handling completely,回到传统的内存管理模型(他们现在称之为“统一内存管理”)。在这种情况下,TObject.Free()
和 FreeAndNil()
现在的行为与它们在非移动平台上的行为相同,但现在在所有平台上都是一样的。所以您的原始代码现在可以按预期工作,您不需要在 10.4 之后切换到 TObject.DisposeOf()
(但是,如果您愿意,您可以这样做,它的行为与 TObject.Free()
完全一样,并为您提供如果您需要支持 10.3 及更早版本,则需要达到预期的效果。
在我的 Firemonkey 多平台应用程序中,我动态创建了一些控件。用完就销毁
在Windows中,这个动态创建和销毁的进程没有问题。但是,似乎在 Android 中,下面的代码不会破坏控件,因为会弹出一个错误提示该控件已经存在(而且当我尝试重新创建时,我确实看到旧控件仍然存在控件)。
如何确保控件在 Android 中被正确销毁(and/or iOS - 我假设它与 iOS 相似)?
for i := oParentRect.ControlsCount-1 downto 0 do
begin
oControl := oParentRect.Controls[i];
if (oControl is TText)
or (oControl is TEdit)
then
begin
FreeAndNil( oControl );
// I have also tried oControl.Free;
end;
end;
在 RAD Studio 10.4 之前,Delphi 在 iOS 和 Android 平台上使用 ARC (Automatic Reference Counting) 进行对象生命周期管理。在 ARC 下,TObject.Free()
和 FreeAndNil()
的行为与您预期的不同。它们被编译器翻译成纯粹的 nil
赋值,减少对象的引用计数。一个对象在其引用计数下降到 0 之前不会被释放。
在你的例子中,有问题的对象有多个引用(在 Controls[]
列表中,在 oControl
变量中),所以 FreeAndNil()
没有达到预期的效果因为它只是将 oControl
变量设置为 nil 而不是从 Controls[]
列表中删除对象。换句话说,您的示例实际上与以下示例相同:
for i := oParentRect.ControlsCount-1 downto 0 do
begin
oControl := oParentRect.Controls[i]; // <-- increments refcnt
if (oControl is TText) or (oControl is TEdit) then
begin
//FreeAndNil( oControl );
oControl := nil; // <-- decrements refcnt
end;
end;
如果你真的想在ARC下编码时立即销毁一个对象,你需要使用TObject.DisposeOf()
来代替,例如:
for i := oParentRect.ControlsCount-1 downto 0 do
begin
oControl := oParentRect.Controls[i]; // <-- increments refcnt
if (oControl is TText) or (oControl is TEdit) then
begin
//FreeAndNil( oControl );
oControl.DisposeOf; // <-- destroys object, but does not free its memory yet
oControl := nil; // <-- decrements refcnt
end;
end;
在这个模型下,当一个对象被“处置”时,它的析构函数会立即被调用,但它的底层内存块还没有被释放。引用计数仍然发生在块上。对象处于 "disposed" 状态,直到其引用计数降为 0,然后释放内存块。不再调用析构函数。
此行为记录在 Embarcadero 的 DocWiki 上:The Free and Dispose Of methods under ARC
现在,话虽这么说,在 RAD Studio 10.4 中,Embarcadero has removed object ARC handling completely,回到传统的内存管理模型(他们现在称之为“统一内存管理”)。在这种情况下,TObject.Free()
和 FreeAndNil()
现在的行为与它们在非移动平台上的行为相同,但现在在所有平台上都是一样的。所以您的原始代码现在可以按预期工作,您不需要在 10.4 之后切换到 TObject.DisposeOf()
(但是,如果您愿意,您可以这样做,它的行为与 TObject.Free()
完全一样,并为您提供如果您需要支持 10.3 及更早版本,则需要达到预期的效果。