-2

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.
TheAviator
  • 41
  • 5
  • 1
    `Application.ProcessMessages`??? Why??? – David Heffernan Mar 26 '16 at 15:34
  • I added Application.ProcessMessages just to see the controls get removed during debug. – TheAviator Mar 26 '16 at 15:49
  • Are we expected to try to recreate your program from your description? I doubt anyone will do that. Can't we have a [mcve]? – David Heffernan Mar 26 '16 at 15:53
  • Of course I can add some more code. I just initially wanted to know if my procedure to remove the iOFPLayout has some mistakes in it. – TheAviator Mar 26 '16 at 15:58
  • Don't drip feed us code. Post a [mcve]. – David Heffernan Mar 26 '16 at 16:17
  • Well, if the code I posted is too much to read I need to make a sample project and add that instead. I don't think the code I posted is so hard to read though. I do not expect anyone to recreate this of course. I just thought there might be some obvious mistakes that someone else easily could spot. On the other hand my test project don't produce this Segmentation fault... – TheAviator Mar 26 '16 at 16:23
  • 2
    Well, there you have it. The final sentence is the key. – David Heffernan Mar 26 '16 at 16:46
  • 1
    If you want some help, show us a small complete program that demonstrates the issue. Arguing isn't constructive. In its current for we should close the question as off topic. *Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers.* http://stackoverflow.com/help/on-topic – David Heffernan Mar 26 '16 at 17:05
  • Have you tried logging of whether your components does indeed gets destroyed or not. You see in ARC based environments components would only get fully destroyed when their reference count reaches 0. If your components does not get fully destroyed you might be running out of memory which is a common cause for `Segmentation fault 11` – SilverWarior Mar 26 '16 at 19:21

1 Answers1

-1

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;
TheAviator
  • 41
  • 5
  • Looks like out of bounds index access here. If you'd have shown a full program then I'm sure we could have taught you plenty. As it is, this is a mess. – David Heffernan Mar 27 '16 at 08:22
  • I might misunderstand you David but as I see it there were no out of bounds index access made. The problem was that the TScrollBox really didn't get destroyed before I started creating new controls. The reference count didn't reach 0. That seem to have caused an out of memory situation. After DisposeOf was called for each dynamically created control that had the TScrollBox as parent the TScollBox finally got destroyed. That solved the problem. SilverWarior pushed me in the right direction. – TheAviator Mar 27 '16 at 10:53
  • The way I understand it my sample code didn't reach this out of memory situation because it didn't use as much memory as the original code. That's why it was a bit useless to post that sample. – TheAviator Mar 27 '16 at 11:04
  • 1
    `for i:=0 to nr do` goes out of bounds I think. There's no out of memory here. You won't post enough code for us to teach you what's really going on, and instead you are making things up that are totally wrong. There's no value in this question for future visitors. You should delete it if you won't fix the question. – David Heffernan Mar 27 '16 at 11:04
  • I hope my new code snippets makes this useful for others. – TheAviator Mar 27 '16 at 12:18
  • And the code you added in the question fails with the error that you report? – David Heffernan Mar 27 '16 at 12:20