Android 动态控制导致分段故障(11)

Android 动态控制导致分段故障(11),android,delphi,firemonkey,delphi-10-seattle,Android,Delphi,Firemonkey,Delphi 10 Seattle,我正在进行一个多平台项目,在这个项目中,我必须根据一些主要的计算结果动态创建许多控件。在更改条件时,我需要删除所有动态创建的控件并进行重新计算,最后再次创建控件 我首先在TTabItem上动态创建一个TScrollBox(iOFPLayout)来处理这个问题。我通过更改预定义标题TToolBar的父项,将其从TTabItem移动到TScrollBox。然后我在TScrollBox上创建TLabel、TEdit和TButton控件的数组。(我需要在代码中与动态创建的控件交互)这一部分在所有平台上都

我正在进行一个多平台项目,在这个项目中,我必须根据一些主要的计算结果动态创建许多控件。在更改条件时,我需要删除所有动态创建的控件并进行重新计算,最后再次创建控件

我首先在TTabItem上动态创建一个TScrollBox(iOFPLayout)来处理这个问题。我通过更改预定义标题TToolBar的父项,将其从TTabItem移动到TScrollBox。然后我在TScrollBox上创建TLabel、TEdit和TButton控件的数组。(我需要在代码中与动态创建的控件交互)这一部分在所有平台上都可以正常工作。当我移除控件时,我使用下面的代码

在Windows x86、x64和OS X上,它似乎工作正常。在Android上,第一次创建TScrollBox并用动态创建的控件填充时,它工作正常。删除并重新创建TScrollBox后,当在TScrollBox顶部创建TLabel控件的动态数组时,在某个随机点出现“访问冲突”或“分段错误(11)”错误

我觉得这和ARC有关。我已经阅读了关于ARC的所有资料,并测试了我能找到的每一条建议,但似乎没有任何效果。有人知道怎么了吗

procedure TTabbedForm.RemoveOFP();
begin
  // Move the ToolBar2 back to TabItem3 so it won’t get destroyed
  ToolBar2.Parent := TabItem3;  
  if Assigned(iOFPLayout) then
  begin
    // iOFPLayout holds a lot of dynamically created controls
    iOFPLayout.Release;
    iOFPLayout.DisposeOf;
    iOFPLayout := nil;
    Application.ProcessMessages;
  end;
end;
下面是一个完整的最小工作示例

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, FMX.Controls.Presentation, FMX.Layouts;

type
  TMainForm = class(TForm)
    ToolBar1: TToolBar;
    Create: TButton;
    Destroy: TButton;
    procedure CreateClick(Sender: TObject);
    procedure DestroyClick(Sender: TObject);
  private
    sb: TScrollBox;
    lbl: array of TLabel;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.fmx}

procedure TMainForm.CreateClick(Sender: TObject);
var
  i: Integer;
begin
  // Create a TScollBox on the MainForm
  sb := TScrollBox.Create(Self);
  sb.Align := TAlignLayout.Client;
  sb.ShowScrollBars := True;
  sb.Parent := MainForm;
  // Set up the number of labels to put on sb
  SetLength(lbl, 1000);
  // Create these labels and set some properties
  for i := Low(lbl) to High(lbl) do
  begin
    // On first run on Android devices this causes no problems.
    // After DestroyClick has been run after the first CreateClick then 
    // I get "Access violation / Segmentation fault (11) here.
    // It happens after about 800+ labels has been created.
    lbl[i] := TLabel.Create(sb);
    lbl[i].Text := 'Label ' + IntToStr(i);
    lbl[i].Position.X := 10;
    lbl[i].Position.Y := i * 20;
    lbl[i].Parent := sb;
  end;
end;

procedure TMainForm.DestroyClick(Sender: TObject);
begin
  if Assigned(sb) then
  begin
    sb.Release;
    sb.DisposeOf;
    sb := nil;
  end;
end;
end.

这个问题的简单答案是,在为TScrollBox本身调用DisposeOf之前,为每个动态创建的控件调用DisposeOf,该控件将TScrollBox作为父控件

procedure TMainForm.DestroyClick(Sender: TObject);
var
  i: Integer;
begin
  if Assigned(sb) then
  begin
    // First call DisposeOf for each child control
    for i := Low(lbl) to High(lbl) do
    begin
      lbl[i].DisposeOf;
    end;
    // Then call DisposeOf for the parent control
    sb.Release;
    sb.DisposeOf;
    sb := nil;
  end;
end;

Application.ProcessMessages
???为什么???我添加Application.ProcessMessages只是为了看到在调试过程中控件被删除。我们是否希望尝试根据您的描述重新创建您的程序?我怀疑有人会那样做。我们不能有一个新的吗?好吧,你有了。最后一句是关键。如果您需要帮助,请向我们展示一个演示该问题的完整小程序。争论是没有建设性的。在目前的情况下,我们应该把这个问题作为离题来结束。寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定的问题或错误以及在问题本身中重现它所需的最短代码。没有明确问题陈述的问题对其他读者没有用处。看起来这里的索引访问超出了范围。如果你有完整的课程,我相信我们可以教你很多。事实上,这是一个混乱。我可能误解了你,大卫,但在我看来,没有越界索引访问。问题是在我开始创建新控件之前,TScrollBox并没有被销毁。引用计数未达到0。这似乎造成了记忆不足的情况。在为每个动态创建的控件调用DisposeOf(该控件将TScollBox作为父控件)后,TScollBox最终被销毁。这就解决了问题。SilverWarior将我推向了正确的方向。按照我的理解,我的示例代码没有达到内存不足的情况,因为它没有使用原始代码那么多的内存。这就是为什么发布那个示例有点没用的原因。
对于i:=0到nr do
我认为超出了范围。这里没有内存不足的问题。你不会发布足够的代码让我们教你到底发生了什么,相反,你在编造完全错误的东西。这个问题对未来的访客没有价值。如果你不想解决这个问题,你应该删除它。我希望我的新代码片段对其他人有用。