peiman F.
peiman F.

Reputation: 1658

Delphi delete FireMonkey element from Form on Android

I created an element on my Form with this code in the OnShow event:

procedure TForm4.FormShow(Sender: TObject);
var
  VertScrollLink:TVertScrollBox;
begin
  VertScrollLink := TVertScrollBox.Create(form4);
  VertScrollLink.Align := TAlignLayout.Client;
  VertScrollLink.Parent := form4;
end;

On some action, I need to delete the layout dynamically:

for LIndex := form4.ComponentCount-1 downto 0 do
begin
  if (form4.Components[LIndex].ToString='TVertScrollBox') then
  begin
    //showmessage(form4.Components[LIndex].ToString);
    form4.Components[LIndex].Free;
  end;
end;

This code works good on Windows, but does not delete anything on Android.

Upvotes: 2

Views: 1507

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595732

The reason is because Delphi uses Automatic Reference Counting for Objects on mobile platforms (iOS and Android), but not on desktop platforms (Windows and OSX). Your Free() is effectively a no-op, because accessing the component from the Components[] property will increment its reference count, and then the Free() will decrement it (in fact, the compiler should have issued a warning about the code having no effect). The component still has active references to it (its Owner and Parent), so it is not actually freed.

If you want to force the component to be freed, you need to call DisposeOf() on it, eg:

for LIndex := form4.ComponentCount-1 downto 0 do
begin
  if form4.Components[LIndex] is TVertScrollBox then
  begin
    form4.Components[LIndex].DisposeOf;
  end;
end;

Alternatively, remove the active references and let ARC handle the destruction normally:

var
  VertScrollLink: TVertScrollBox;
  LIndex: Integer;
begin
  ...
  for LIndex := form4.ComponentCount-1 downto 0 do
  begin
    if form4.Components[LIndex] is TVertScrollBox then
    begin
      VertScrollLink := TVertScrollBox(form4.Components[LIndex]);
      VertScrollLink.Parent := nil;
      VertScrollLink.Owner.RemoveComponent(VertScrollLink);
      VertScrollLink := nil;
    end;
  end;
  ...
end;

That being said, you might consider keeping track of the component you create so you don't need to use a loop to find it later:

type
  TForm4 = class(TForm)
    procedure FormShow(Sender: TObject);
    ...
  private
    VertScrollLink: TVertScrollBox;
    ...
  end;

procedure TForm4.FormShow(Sender: TObject);
begin
  VertScrollLink := TVertScrollBox.Create(Self);
  VertScrollLink.Align := TAlignLayout.Client;
  VertScrollLink.Parent := Self;
end;

begin
  ...
  if Assigned(VertScrollLink) then
  begin
    VertScrollLink.DisposeOf;
    { or:
    VertScrollLink.Parent := nil;
    VertScrollLink.Owner.RemoveComponent(VertScrollLink);
    }
    VertScrollLink := nil;
  end;
  ...
end;

Upvotes: 6

Related Questions