Search code examples
androiddelphifiremonkeydelphi-10-seattle

Dynamic controls gives Segmentation fault (11)


I’m working on a multiplatform project where I have to create lots of controls dynamically as a result of some major calculations. When changing the conditions, I need to remove all dynamically created controls and make the recalculations and finally create the controls again.

I handle this by first dynamically create a TScrollBox (iOFPLayout) on top of a TTabItem. I move the predefined header TToolBar from the TTabItem to the TScrollBox by changing its parent. Then I create arrays of TLabel, TEdit and TButton controls on the TScrollBox. (I need to interact with the dynamically created controls in code) This part works fine on all platforms. When I remove the controls I use the code below.

On Windows x86, x64 and OS X it seems to work fine. On Android it works fine the first time the TScrollBox is created and filled with dynamically created controls. After the TScrollBox has been removed and recreated I get an “Access Violation” or “Segmentation fault (11)” error at some random point when the dynamic arrays of TLabel controls are created on top of the TScrollBox.

I get a feeling that this has to do with ARC. I have read everything I can about ARC and tested every suggestion I can find but nothing seem to work. Does anyone see what’s wrong?

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;

Here is a complete minimal working example.

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.

Solution

  • The simple answer to this question is to call DisposeOf for each dynamically created control that has the TScrollBox as parent before calling DisposeOf for the TScrollBox itself.

    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;