Delphi Windows Firemonkey 下的 ARC
Delphi ARC under Firemonkey for Windows
我正在观看 this 视频,其中 Marco 正在谈论自动引用计数。我已经知道在 Android 和 iOS (Firemonkey) 下我的对象被引用计数所以我不需要 try finally
块。
引用计数实现是否根据平台(VLC 或 FMX)或 OS?
我的意思是:
var a: TObject;
begin
a := TObject.Create;
a.use1;
a.use2;
end;
如果它在 Android/iOS/Mac 上运行,这在 Firemonkey 中很好,我没有内存泄漏。但是,如果我 运行 在 Windows 下(并且我使用了 Firemonkey),我是否仍然会因为没有引用计数而导致内存泄漏?
无论如何,从视频中我了解到,即使在 ARC 下,调用 Free
的 try finally
也不是坏用法,但它只是没用。
ARC 是根据 OS 平台而不是 GUI 框架实现的。
Android、iOS 和 Linux 编译器对对象使用 ARC 内存管理。 Windows 和 OSX 编译器使用经典的手动内存管理,其中 ARC 仅支持接口引用,而不支持对象。
VCL 是一个 Windows-only 框架,它只能在经典编译器下运行。
另一方面,FMX 作为一个跨平台框架,使用不同的内存管理系统,这取决于它在哪个 OS 平台上运行。
try...finally Free
块在 ARC 编译器上确实无用(在安全保护对象释放与 Free
方法结合的上下文中)。
但是,如果您编写的跨平台代码必须在两种内存管理系统下工作,则必须使用 try...finally
和 Free
。
另一方面,如果您编写的代码只能在 ARC 下工作,则可以安全地省略 try...finally
和 Free
。
然而,在 ARC 上你可能想使用 Free
(在 ARC 编译器上它转换为 nil
赋值)或者直接将 nil
赋值给 object/interface 引用,如果你需要在某个点释放对象,以免它的引用超出范围。
上述规则有一个重要的例外 - TComponent
后代(包括 Firemonkey GUI 组件和控件),其中 try...finally Free
块必须替换为 try...finally DisposeOf
如果您是在代码中创建和释放这些实例。
您可以在 How to free a component in Android / iOS
阅读更多内容
这里需要注意的重要事项:DisposeOf
有非常特殊的用途,它不是打破引用循环和释放 ARC 下对象的通用解决方案。在所有地方盲目使用它会导致内存泄漏。可以在上面 Q/A DisposeOf 的陷阱
下找到更详尽的答案
引用计数 ARC 在 delphi 下非常令人困惑,它使开发变得更加困难,因为它设计糟糕(在 delphi 下)。
首先,它是在 RTL 中构建的,它与您使用的 String 非常相似。它适用于 plateform (firemonkey),也适用于 OS(仅适用于 android/ios/unix)。因此,如果您想构建还针对 window(最有可能,至少只是为了调试您的代码) 的多平台代码,您无论如何都需要继续尝试……最后……结束; (是的,我说你,非常糟糕的设计)。所以忘记你没有 .free 的例子,除非你想在移动设备下调试(祝你好运,每次编译 3 分钟,这根本不可能)
注意:为了让你的代码尽可能兼容,一个好的规则是用.disposeOF替换你所有的.free,然后是nil 因为在移动设备下,free 是一个无操作操作,你的对象不会被销毁,并且可以在非常意外的时间被销毁(或者在最坏的情况下根本不会被销毁,如果你使用例如 TTask 就很常见) .这些情况并不少见,特别是如果您经常使用在后台捕获到您的对象时创建的匿名过程(对过程的引用)。
Always keep in mind that circular ref are quite easy to meet and quite
hard to detect
.
在你必须知道之后(你没有问,但我扩展了一点答案)他们是 delphi Tobject,java object,IOS objective C 对象和接口。所有这些都有自己的规则,让每个人都感到困惑,最后没有人真正知道它是如何工作的(是的,ARC 糟糕设计的一部分也是它所带来的混乱),甚至 emb 开发人员也会在他们的 delphi 源代码中犯错代码,寻找这个问题的例子:delphi + ios: release / retain and reference counting with objective-c object 这很简单,但没有任何答案
ARC 和 Objective-C 包裹对象
Delphi NextGen 编译器为所有 Delphi 对象实现了自动引用计数 (ARC)。编译器将为您管理 TObject 的 __ObjAddRef 和 __ObjRelease 的逻辑。 Objective-C 代码使用相同的逻辑来调用保留和释放。不幸的是,由导入包装器 class 和上面讨论的接口表示的 Objective-C 对象没有 ARC。在处理 Objective-C 对象时,你必须在正确的点调用 retain 和 release 自己。
ARC 和 JAVA 对象
在纸上它必须工作,但我个人不相信它,例如,如果你在循环中做:
for i := 0 to 100000 do begin
aJstring := StringToJstring('a text');
aStr := JstringToString(aJstring);
end;
通常这必须 运行 在正常世界中没有任何问题,但在 delphi 下它会崩溃 :( 但无论如何你没有 .release 所以你没有选择(除了将变量分配给 nil)。但是当你有选择时,为什么 我建议总是使用 .disposeOF 后跟 nil,你可能会赢得 days/weeks/month 的开发转义一些讨厌的错误。
注意:当我想销毁一个对象时调用这个函数:
{******************************}
Procedure ALFreeAndNil(var Obj);
var Temp: TObject;
begin
Temp := TObject(Obj);
if temp = nil then exit;
TObject(Obj) := nil;
{$IF defined(AUTOREFCOUNT)}
if AtomicCmpExchange(temp.refcount{Target}, 0{NewValue}, 0{Compareand}) = 1 then begin // it's seam it's not an atomic operation (
temp.Free;
temp := nil;
end
else begin
Temp.DisposeOf; // TComponent Free Notification mechanism notifies registered components that particular
// component instance is being freed. Notified components can handle that notification inside
// virtual Notification method and make sure that they clear all references they may hold on
// component being destroyed.
//
// Free Notification mechanism is being triggered in TComponent destructor and without DisposeOf
// and direct execution of destructor, two components could hold strong references to each
// other keeping themselves alive during whole application lifetime.
{$IF defined(DEBUG)}
if (Temp.RefCount - 1) and (not 000000{Temp.objDisposedFlag}) <> 0 then
ALLog('ALFreeAndNil', Temp.ClassName + ' | Refcount is not null (' + Inttostr((Temp.RefCount - 1) and (not 000000{Temp.objDisposedFlag})) + ')', TalLogType.warn);
{$IFEND}
temp := nil;
end;
{$ELSE}
temp.Free;
temp := nil;
{$IFEND}
end;
这样,如果在调用 ALFreeAndNil 后引用计数不为 0,则会在日志中生成警告(正在调试),您可以调查
我正在观看 this 视频,其中 Marco 正在谈论自动引用计数。我已经知道在 Android 和 iOS (Firemonkey) 下我的对象被引用计数所以我不需要 try finally
块。
引用计数实现是否根据平台(VLC 或 FMX)或 OS?
我的意思是:
var a: TObject;
begin
a := TObject.Create;
a.use1;
a.use2;
end;
如果它在 Android/iOS/Mac 上运行,这在 Firemonkey 中很好,我没有内存泄漏。但是,如果我 运行 在 Windows 下(并且我使用了 Firemonkey),我是否仍然会因为没有引用计数而导致内存泄漏?
无论如何,从视频中我了解到,即使在 ARC 下,调用 Free
的 try finally
也不是坏用法,但它只是没用。
ARC 是根据 OS 平台而不是 GUI 框架实现的。
Android、iOS 和 Linux 编译器对对象使用 ARC 内存管理。 Windows 和 OSX 编译器使用经典的手动内存管理,其中 ARC 仅支持接口引用,而不支持对象。
VCL 是一个 Windows-only 框架,它只能在经典编译器下运行。
另一方面,FMX 作为一个跨平台框架,使用不同的内存管理系统,这取决于它在哪个 OS 平台上运行。
try...finally Free
块在 ARC 编译器上确实无用(在安全保护对象释放与 Free
方法结合的上下文中)。
但是,如果您编写的跨平台代码必须在两种内存管理系统下工作,则必须使用 try...finally
和 Free
。
另一方面,如果您编写的代码只能在 ARC 下工作,则可以安全地省略 try...finally
和 Free
。
然而,在 ARC 上你可能想使用 Free
(在 ARC 编译器上它转换为 nil
赋值)或者直接将 nil
赋值给 object/interface 引用,如果你需要在某个点释放对象,以免它的引用超出范围。
上述规则有一个重要的例外 - TComponent
后代(包括 Firemonkey GUI 组件和控件),其中 try...finally Free
块必须替换为 try...finally DisposeOf
如果您是在代码中创建和释放这些实例。
您可以在 How to free a component in Android / iOS
阅读更多内容这里需要注意的重要事项:DisposeOf
有非常特殊的用途,它不是打破引用循环和释放 ARC 下对象的通用解决方案。在所有地方盲目使用它会导致内存泄漏。可以在上面 Q/A DisposeOf 的陷阱
引用计数 ARC 在 delphi 下非常令人困惑,它使开发变得更加困难,因为它设计糟糕(在 delphi 下)。
首先,它是在 RTL 中构建的,它与您使用的 String 非常相似。它适用于 plateform (firemonkey),也适用于 OS(仅适用于 android/ios/unix)。因此,如果您想构建还针对 window(最有可能,至少只是为了调试您的代码) 的多平台代码,您无论如何都需要继续尝试……最后……结束; (是的,我说你,非常糟糕的设计)。所以忘记你没有 .free 的例子,除非你想在移动设备下调试(祝你好运,每次编译 3 分钟,这根本不可能)
注意:为了让你的代码尽可能兼容,一个好的规则是用.disposeOF替换你所有的.free,然后是nil 因为在移动设备下,free 是一个无操作操作,你的对象不会被销毁,并且可以在非常意外的时间被销毁(或者在最坏的情况下根本不会被销毁,如果你使用例如 TTask 就很常见) .这些情况并不少见,特别是如果您经常使用在后台捕获到您的对象时创建的匿名过程(对过程的引用)。
Always keep in mind that circular ref are quite easy to meet and quite hard to detect
.
在你必须知道之后(你没有问,但我扩展了一点答案)他们是 delphi Tobject,java object,IOS objective C 对象和接口。所有这些都有自己的规则,让每个人都感到困惑,最后没有人真正知道它是如何工作的(是的,ARC 糟糕设计的一部分也是它所带来的混乱),甚至 emb 开发人员也会在他们的 delphi 源代码中犯错代码,寻找这个问题的例子:delphi + ios: release / retain and reference counting with objective-c object 这很简单,但没有任何答案
ARC 和 Objective-C 包裹对象
Delphi NextGen 编译器为所有 Delphi 对象实现了自动引用计数 (ARC)。编译器将为您管理 TObject 的 __ObjAddRef 和 __ObjRelease 的逻辑。 Objective-C 代码使用相同的逻辑来调用保留和释放。不幸的是,由导入包装器 class 和上面讨论的接口表示的 Objective-C 对象没有 ARC。在处理 Objective-C 对象时,你必须在正确的点调用 retain 和 release 自己。
ARC 和 JAVA 对象
在纸上它必须工作,但我个人不相信它,例如,如果你在循环中做:
for i := 0 to 100000 do begin
aJstring := StringToJstring('a text');
aStr := JstringToString(aJstring);
end;
通常这必须 运行 在正常世界中没有任何问题,但在 delphi 下它会崩溃 :( 但无论如何你没有 .release 所以你没有选择(除了将变量分配给 nil)。但是当你有选择时,为什么 我建议总是使用 .disposeOF 后跟 nil,你可能会赢得 days/weeks/month 的开发转义一些讨厌的错误。
注意:当我想销毁一个对象时调用这个函数:
{******************************}
Procedure ALFreeAndNil(var Obj);
var Temp: TObject;
begin
Temp := TObject(Obj);
if temp = nil then exit;
TObject(Obj) := nil;
{$IF defined(AUTOREFCOUNT)}
if AtomicCmpExchange(temp.refcount{Target}, 0{NewValue}, 0{Compareand}) = 1 then begin // it's seam it's not an atomic operation (
temp.Free;
temp := nil;
end
else begin
Temp.DisposeOf; // TComponent Free Notification mechanism notifies registered components that particular
// component instance is being freed. Notified components can handle that notification inside
// virtual Notification method and make sure that they clear all references they may hold on
// component being destroyed.
//
// Free Notification mechanism is being triggered in TComponent destructor and without DisposeOf
// and direct execution of destructor, two components could hold strong references to each
// other keeping themselves alive during whole application lifetime.
{$IF defined(DEBUG)}
if (Temp.RefCount - 1) and (not 000000{Temp.objDisposedFlag}) <> 0 then
ALLog('ALFreeAndNil', Temp.ClassName + ' | Refcount is not null (' + Inttostr((Temp.RefCount - 1) and (not 000000{Temp.objDisposedFlag})) + ')', TalLogType.warn);
{$IFEND}
temp := nil;
end;
{$ELSE}
temp.Free;
temp := nil;
{$IFEND}
end;
这样,如果在调用 ALFreeAndNil 后引用计数不为 0,则会在日志中生成警告(正在调试),您可以调查