如何在Android/iOS中释放组件

如何在Android/iOS中释放组件,android,ios,delphi,components,firemonkey,Android,Ios,Delphi,Components,Firemonkey,我在Android中的表单上动态创建了一个TEdit: edit := TEdit.Create(Self); 我想使用edit.free释放它,但它仍然在表单上 此代码在win32上运行良好,但在Android上失败 这似乎不仅适用于TEdit,也适用于使用Android或iOS的任何组件。在移动平台上,使用ARC管理生命周期。只有当对象没有引用时,对象才会被销毁。您的对象具有对它的引用,特别是来自其父对象的引用 现在可以使用DisposeOf强制销毁对象。详情如下: 但是,我怀疑更好的解决

我在Android中的表单上动态创建了一个
TEdit

edit := TEdit.Create(Self);
我想使用
edit.free
释放它,但它仍然在表单上

此代码在win32上运行良好,但在Android上失败


这似乎不仅适用于TEdit,也适用于使用Android或iOS的任何组件。

在移动平台上,使用ARC管理生命周期。只有当对象没有引用时,对象才会被销毁。您的对象具有对它的引用,特别是来自其父对象的引用

现在可以使用
DisposeOf
强制销毁对象。详情如下:

但是,我怀疑更好的解决方案是删除对对象的引用。将其从容器中取出。例如,将其父对象设置为nil

更新为10.4 Delphi 10.4 Sydney跨所有平台统一内存管理,并删除了ARC编译器。换句话说,所有平台现在都遵循与Windows平台相同的内存管理规则

dispeof
vs
Free
在经典(非ARC)编译器中

  • DisposeOf
    对经典编译器调用
    Free
    进行处理,在功能上表现相同
  • DisposeOf
    仅用于向后兼容,在新代码(不必与ARC编译器保持兼容)中,首选使用
    Free
  • 在现有代码中,
    DisposeOf
    不必更改为
    Free

原始答案,对ARC编译器有效: 简短回答 在Delphi ARC编译器(当前为Android和iOS)下发布任何
TComponent
子体对象时,应遵循两条规则:

  • 无论对象是否拥有所有者,都必须使用
    DisposeOf
  • 在析构函数中或在调用
    DisposeOf
    后不久引用未超出范围的情况下,对象引用也应设置为
    nil
    (陷阱中的详细说明)
使用
DisposeOfAndNil
方法可能很有吸引力,但ARC使其比旧的
FreeAndNil
方法复杂得多,我建议使用普通的
DisposeOf-nil
顺序来避免其他问题:

Component.DisposeOf;
Component := nil;
虽然在许多情况下,即使不遵守上述规则,代码也能正常工作,但这样的代码相当脆弱,很容易被在看似无关的地方引入的其他代码破坏

ARC内存管理上下文中的DisposeOf
DisposeOf
断开圆弧。它违反了ARC的黄金法则任何对象引用都可以是有效的对象引用或nil,并引入了第三状态-已处置的“僵尸”对象引用

任何试图理解ARC内存管理的人都应该看看类似于添加的
dispeof
,它只是解决了特定于Delphi的框架问题,而不是真正属于ARC本身的概念

为什么Delphi ARC编译器中存在DisposeOf?
t组件
类(及其所有子类)的设计考虑了手动内存管理。它使用的通知机制与ARC内存管理不兼容,因为它依赖于中断析构函数中的强引用循环。由于
TComponent
是Delphi框架所依赖的基类之一,因此它必须能够在ARC内存管理下正常工作

除了
自由通知
机制之外,Delphi框架中还有其他类似的设计适合于手动内存管理,因为它们依赖于中断析构函数中的强引用循环,但这些设计不适合于ARC

DisposeOf
方法允许直接调用对象析构函数,并允许此类遗留代码与ARC一起使用

这里必须注意一点。任何使用或继承自
TComponent
的代码都会在正确的ARC管理环境中自动成为遗留代码,即使您现在就编写

引用艾伦·鲍尔的博客

那么,脱节还能解决什么呢?这在各种各样的人中很常见 Delphi框架(包括VCL和FireMonkey),用于将 构造函数内的通知或列表管理代码,以及 类的析构函数。TComponent的所有者/所有者模型是一个关键 这种设计的例子。在本例中,现有组件 框架设计依赖于许多活动,而不是简单的“资源” “管理”将发生在析构函数中

TComponent.Notification()就是这样一个关键的例子。在这个 在这种情况下,“处置”组件的正确方法是使用DisposeOf。A. t组件派生通常不是一个瞬态实例,而是一个瞬态实例 一种寿命较长的物体,周围还有一个完整的生命系统 构成形状、框架等的其他构件实例 和数据模块。在这种情况下,使用DisposeOf是合适的

处置是如何运作的 为了更好地理解调用
DisposeOf
时到底发生了什么,有必要了解Delphi对象销毁过程是如何工作的

在ARC和非ARC Delphi编译器中释放对象涉及三个不同的阶段

  • 调用
    析构函数Destroy
    方法链
  • 清理对象管理的字段-字符串、接口、动态数组(在包含普通对象引用的ARC编译器下)
  • 从堆中释放对象内存
  • 使用非ARC编译器释放对象

    Component.Free
    ->立即执行阶段
    1->2->3

    使用ARC编译器释放对象

    • Component.Free
      Component:=nil
      ->减少对象引用计数,后跟a)b)

      • a)如果对象参考c
        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;
        
        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;
        
        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;
        
        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;
        
          TChild = class(TObject)
          public
            [weak] var Parent: TParent;
            constructor Create(AParent: TParent);
          end;