如何免费的Andr​​oid / iOS的一个组成部分组成部分、Andr、oid、iOS

2023-09-12 03:22:02 作者:可爱到发芽

我动态创建一个 TEDIT Android中的窗体上:

I dynamically create a TEdit on a form in Android:

edit := TEdit.Create(Self);

我要释放它使用 edit.Free ,但它只是仍然在形式。

I want to free it using edit.Free, but it just still on form.

这code正常工作在Win32,但未能在Android上。

This code works fine on win32, but failed on Android.

同样似乎发生不仅使用Android或iOS的为TEDIT但对于任何部件。

The same seems to happen not only for TEdit but for any component using Android or iOS.

推荐答案

有,应该释放在德尔福ARC的编译器(目前Android和iOS)的任何 TComponent的后代对象时要遵循两个规则:

Short Answer

There are two rules that should be followed when releasing any TComponent descendant object under Delphi ARC compilers (currently Android and iOS):

使用 DisposeOf 是具有所有者或对象不强制,无论 在析构函数或如引用未不久 DisposeOf 走出去的范围称为,对象的引用也应设置为(在缺陷详细的说明) using DisposeOf is mandatory regardless of object having owner or not in destructors or in cases where reference is not going out of scope shortly after DisposeOf is called, object reference should also be set to nil (detailed explanation in Pitfalls)

这可能是吸引具有 DisposeOfAndNil 的方法,但ARC使得它远远比它更复杂是旧的情况下 FreeAndNil 的方法,我会建议使用普通的 DisposeOf - 零序列,以避免其他问题:

It may be appealing to have DisposeOfAndNil method, but ARC makes it far more complicated than it was the case with old FreeAndNil method and I would suggest using plain DisposeOf - nil sequence to avoid additional issues:

Component.DisposeOf;
Component := nil;

而在许多情况下,code将正常工作,即使不遵守以上规则,例如code是相当脆弱的,很容易在看似无关的地方引入其他code被破坏。

While in many cases code will function properly even if above rules are not followed, such code would be rather fragile and could easily be broken by other code introduced in seemingly unrelated places.

DisposeOf 打破ARC。它违反了ARC的金科玉律的任何对象的引用可以是有效的对象引用或零并引入第三种状态 - 处理僵尸的对象引用。

DisposeOf breaks ARC. It violates golden rule of ARC Any object reference can be either valid object reference or nil and introduces third state - disposed "zombie" object reference.

任何人试图了解ARC内存管理应该看看 DisposeOf 类似此外,仅仅解决了德尔福具体框架问题,而不是概念,真正属于电弧本身。

Anyone trying to understand ARC memory management should look at DisposeOf like addition that just solves Delphi specific framework issues and not concept that really belongs to ARC itself.

TComponent的类(及其所有后代)在设计时考虑到手动内存管理。它使用的是不与ARC内存管理兼容,因为它依赖于打破很强的参考周期的析构函数通知机制。由于 TComponent的是基类德尔福框架依靠之一,它必须能够根据ARC内存管理才能正常工作。

TComponent class (and all its descendants) was designed with manual memory management in mind. It uses notification mechanism that is not compatible with ARC memory management because it relies on breaking strong reference cycles in destructor. Since TComponent is one of base classes Delphi frameworks rely upon, it must be able to function properly under ARC memory management.

除了免费通知机制还有德尔福其他类似的设计框架,适合手工内存管理,但这些设计不适合为ARC。

Besides Free Notification mechanism there are other similar designs in Delphi frameworks suitable for manual memory management because they rely on breaking strong reference cycles in destructor, but those designs are not suitable for ARC.

DisposeOf 方法使对象的析构函数的直接呼叫,使这类的遗留code 的与ARC一起玩。

DisposeOf method enables direct calling of object destructor and enables such legacy code to play along with ARC.

有一点必须注意。使用或者从 TComponent的继承自动成为任何code的遗留code 的在适当的ARC管理的背景下,即使你今天把它写。

One thing must be noted here. Any code that uses or inherits from TComponent automatically becomes legacy code in context of proper ARC management even if you write it today.

这是艾伦·鲍尔的博客给到ARC侧

Quote from Allen Bauer's blog Give in to the ARC side

那么还有什么不DisoseOf解决?它是在各种非常普遍   德尔福框架(VCL和FireMonkey在内),把活跃   在构造函数中通知或名单管理code和   析构函数的一类。 TComponent的所有者/自有模型是关键   例的这种设计。在这种情况下,现有的组件   架构设计依赖于不是简单的资源等诸多活动   管理的情况发生在析构函数。

So what else does DisoseOf solve? It is very common among various Delphi frameworks (VCL and FireMonkey included), to place active notification or list management code within the constructor and destructor of a class. The Owner/Owned model of TComponent is a key example of such a design. In this case, the existing component framework design relies on many activities other than simple "resource management" to happen in the destructor.

TComponent.Notification()就是这样的事情一个重要的例子。在这   情况下,正确的方式来处置的组成部分,是使用DisposeOf。一个   TComponent的衍生物通常不是瞬态的实例,而它是   其周围也对整个系统寿命更长的对象   其他组件实例构成的东西,如表格,框架   和datamodules。在这种情况下,使用DisposeOf是适当的。

TComponent.Notification() is a key example of such a thing. In this case, the proper way to "dispose" a component, is to use DisposeOf. A TComponent derivative isn’t usually a transient instance, rather it is a longer-lived object which is also surrounded by a whole system of other component instances that make up things such as forms, frames and datamodules. In this instance, use DisposeOf is appropriate.

要具有更好的理解到底发生了什么,当 DisposeOf 被调用时,有必要知道如何德尔福对象销毁处理工作。

How DisposeOf works

To have better understanding of what exactly happens when DisposeOf is called, it is necessary to know how Delphi object destruction process works.

有涉及这两个ARC和非ARC的Delphi编译器释放对象三个不同的阶段。

There are three distinct stages involved in releasing object in both ARC and non-ARC Delphi compilers

调用析构函数销毁方法链 在清理对象管理领域 - 串,接口,动态数组(根据ARC的编译器,它包括普通对象引用,太) 从堆中释放对象的内存 calling destructor Destroy methods chain cleaning up object managed fields - strings, interfaces, dynamic arrays (under ARC compiler that includes plain object references, too) releasing object memory from the heap

与非ARC的编译器的释放对象

Releasing object with non-ARC compilers

Component.Free - >期 1的立即执行 - > 2 - > 3

与ARC的编译器的释放对象

Component.Free 组件:=零 - >减少对象的引用计数,然后按一)或 B)

Component.Free or Component := nil -> decreases object reference count followed by a) or b) )如果对象的引用计数为0 - 阶段 1>立即执行 - > 2 - > 3 B)如果对象的引用计数大于0没有什么事情发生

a) if object reference count is 0 -> immediate execution of stages 1 -> 2 -> 3 b) if object reference count is greater than 0 nothing else happens

Component.DisposeOf - >阶段立即执行 1 ,阶段 2 3 将在以后执行时的对象引用计数到达0 DisposeOf 不降低呼叫参考的引用计数。

Component.DisposeOf -> immediate execution of stage 1, stages 2 and 3 will be executed later when objects reference count reaches 0. DisposeOf does not decrease reference count of calling reference.

TComponent的通知系统

TComponent的 免费通知机制通知特定组件实例将被释放注册的组件。通知组件可以处理内部虚拟通知方法的通知,并确保他们清楚,他们可能持有的所有引用的成分被破坏。

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.

在非ARC的编译器的机制可以确保您不会结束与悬摆指针指向无效 - 发行对象,并根据ARC的编译器清除引用销毁部分将减少它的引用计数,并打破有力的参考周期

Under non-ARC compilers that mechanism ensures that you don't end up with dangling pointers pointing to invalid - released objects and under ARC compilers clearing references to destroying component will decrease its reference count and break strong reference cycles.

免费通知机制被触发 TComponent的析构函数,没有 DisposeOf 键,直接执行析构函数,两个组成部分可以容纳整个应用程序生命周期内相互保持自己活着强引用。

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.

FFreeNotifies 列表保存感兴趣的通知组件列表中被声明为 FFreeNotifies:从TList< TComponent的> 并会储存强引用任何注册的组件。

FFreeNotifies list that holds list of components interested in notification is declared as FFreeNotifies: TList<TComponent> and it will store strong reference to any registered component.

因此​​,举例来说,如果你有 TEDIT TPopupMenu 的形式和分配的弹出式菜单中进行编辑的的PopupMenu 属性,编辑将持有强引用弹出在其 FEditPopupMenu 字段菜单,弹出菜单将持有强引用编辑其 FFreeNotifies 列表。如果你想发布这两个组件要么你得叫 DisposeOf 对他们或他们只会继续存在。

So for instance if you have TEdit and TPopupMenu on your form and assign that popup menu to edit's PopupMenu property, edit will hold strong reference to popup menu in its FEditPopupMenu field, and popup menu will hold strong reference to edit in its FFreeNotifies list. If you want to release either of those two components you have to call DisposeOf on them or they will just continue to exist.

虽然你可以尝试手动跟踪这些连接,并打破有力的参考周期,然后再释放任何可能不那么容易做到在实践中这些对象。

While you can try to track those connections manually and break strong reference cycles before you release any of those objects that may not be so easy to do in practice.

随着code基本上会泄漏ARC下的两个组成部分,因为它们将持有强引用对方,过程完成后,你将不再有指向任何一个这些组件的任何外部引用。但是,如果替换 Menu.Free Menu.DisposeOf 你会触发免费通知机制,打破有力的参考周期。

Following code will basically leak both components under ARC because they will hold strong reference to each other, and after procedure is finished you will no longer have any external references that point to either one of those components. However, if you replace Menu.Free with Menu.DisposeOf you will trigger Free Notification mechanism and break strong reference cycle.

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 实施开发人员应该知道的。

Pitfalls of DisposeOf

Besides breaking ARC, that is bad on its own, because when you break it you don't have much use of it, there are also two major issues with how DisposeOf is implemented that developers should be aware of.

1。 DisposeOf 并没有减少对呼叫参考的质检报告#126241

1. DisposeOf does not decrease reference count on calling reference QC report #126241

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 不清理实例内部管理类型的引用的质检报告#126243

2. DisposeOf does not clean up instance inner managed types references QC report #126243

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;

以上问题共同作用可以体现在不同的方式。从保持分配更多的内存比必要的努力来赶上是由包含非所属的对象和接口引用错了,意想不到的引用计数造成的错误。

Combined effect of above issues can manifest itself in different manners. From keeping more allocated memory than necessary to hard to catch bugs that are caused by wrong, unexpected reference count of contained non-owned object and interface references.

由于 DisposeOf 不降低要求引用它的引用计数是很重要的的析构函数,否则整个这样的参考对象层次结构可以留活活远长于需要,在某些情况下,甚至在整个应用程序生存期。

Since DisposeOf does not decrease reference count of calling reference it is important to nil such reference in destructors otherwise whole object hierarchies can stay up alive much longer than needed and in some cases even during the whole application lifetime.