【问题标题】:Is it safe to store visual components in a dynamic array?将可视组件存储在动态数组中是否安全?
【发布时间】:2018-05-08 04:45:52
【问题描述】:

在delphi中,如果动态数组是引用计数的,那么将可视组件存储在数组中是否安全,引用计数器如何与可视组件一起使用?引用计数器是否会被欺骗,因为可视组件与常规的非可视代码有很大不同...

我不知道较新的 delphi 副本中的动态数组是否以引用计数以外的其他方式进行内存管理,例如在幕后以某种 List 的形式实现。

【问题讨论】:

    标签: delphi


    【解决方案1】:

    这是绝对安全的。就如何处理实例变量而言,可视组件与非可视组件并没有什么不同。

    动态数组的引用计数可以简单地认为是数组的自动内存管理。请注意,我说的是“数组”。动态数组的引用计数并不意味着其元素的内存管理。

    TMyVisualComponent 的动态数组的元素只是一个保存对对象的引用的变量。它与TMyVisualComponent 类型的局部变量或TMyVisualComponent 类型的类的字段没有什么不同。

    考虑以下两个例子:

    type
      TMyClass1 = class
      private
        FObj: TMyVisualComponent;
      end;
    
      TMyClass2 = class
      private
        FArr: TArray<TMyVisualComponent>;
      end;
    

    FObj 中放置一个对实例的引用,该实例的生命周期在别处进行管理。同样,在FArr 中放置了对多个实例的引用,这些实例的生命周期在其他地方进行管理。

    TMyClass1 的实例被销毁时,FObj 引用的实例不会发生任何事情。所发生的只是TMyClass1 的实例被销毁,并且由于它包含变量FObj,因此该变量也被销毁。请注意,我说的是“变量”。变量FObj 被销毁,它所引用的对象不受影响。

    同样,当TMyClass2 的实例被销毁时,数组FArr 也被销毁,但它所引用的对象没有任何反应。

    最后一个问题是,在 ARC 编译器下,所有对类的引用都是引用计数的。所以我上面所说的需要修改为包括TMyVisualComponent实例的引用计数。但由于还有其他方持有对这些 TMyVisualComponent 实例的引用,因此它们将始终保持活跃。

    总而言之,简单地将TArray&lt;TMyVisualComponent&gt; 视为在同一范围内定义的TMyVisualComponent 的多个变量。

    【讨论】:

    • 您可能需要补充一点,ARC 编译器中不存在 VCL 组件,因为 VCL 仅适用于 Windows。
    • @Johan 问题中没有任何内容表明 VCL 或 FMX。
    【解决方案2】:

    Delphi 中只有有限的一组元素被引用计数。
    VCL 组件不是。它们不会通过引用计数自动销毁。 VCL 组件不使用引用计数,而是使用ownership 的概念。 VCL 组件的所有者负责销毁它拥有的所有组件。

    在数组中存储(引用)VCL 组件是非常安全的,无论是动态的还是其他的。

    动态数组与它们在 D4 中引入以来的状态相同。一个简单的数组,分配在堆上,是引用计数的。

    请注意,包含项目的数组被引用计数这一事实不会以任何方式影响其中包含的事物。如果您在其中存储更多项目,则数组的引用计数不会增加。仅当您增加对数组的引用数时,它才会上升,如下所示:

    function Test: TArray<TComponent>;
    var
      a: TArray<TComponent>
    begin
      SetLength(a, 100);  //first use of a, refcount = 1
      a[0]:= Button1;  //nothing happens
      Result:= a;   //increases refcount of a to 2.
    end; //end of scope for a, refcount of a decreased to 1.
    
    procedure Test2;
    var
      x: TArray<TComponent>
    begin
      x:= Test;  //x = Test.a, refcount of x = 1
      x[1]:= Button2; //nothing happens
    end; //end of scope for x, refcount = 0, x is freed.
    //nothing happens to button1 and button2, because VCL components are not reference counted.
    

    如果您将引用计数的项目存储在动态数组中,那么当动态数组被销毁时,它们的引用计数将减少(如果这会使它们的引用计数为 0,则将它们销毁)。引用的类型有:

    • 字符串
    • 动态数组(是的,您可以嵌套它们)
    • 接口
    • ARC 编译器中的类(Mobile、Linux)

    请注意,虽然您可以获得对 VCL 类的接口引用,但 VCL 类的引用计数固定为 -1(这意味着 _Addref_Release 是什么都不做的虚拟方法)。

    【讨论】:

    • 难以理解的概念是,在 Test2 中,数组消失了,但是如果您将 Button 创建为数组的一部分,那么人类(我的)大脑会将数组视为存储按钮结构。该按钮仍在使用的屏幕上,但是,它存储的数组被破坏,这似乎创建了一个无效的按钮,但它不是无效的,因为 Windows 操作系统接管了该按钮并且该按钮实际上并未被存储在数组中,对象只是一个指针。我将不得不研究 TArray 和 Array of TComponent 之间的区别:听起来像泛型
    • type TArray&lt;TComponent&gt; = array of TComponent。但更好的是,因为TArray&lt;x&gt; 避免了array of x 作为类型时的动态数组和array of x 作为开放数组参数之间的混淆。 Tbutton 并非没有对它的引用,因为它仍然归包含它的表单所有。
    猜你喜欢
    • 1970-01-01
    • 2018-07-19
    • 2020-08-06
    • 2017-04-12
    • 2018-07-11
    • 2016-09-17
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    相关资源
    最近更新 更多