动态控制给出分段错误(11)
Dynamic controls gives Segmentation fault (11)
我正在从事一个多平台项目,在该项目中,由于一些主要计算,我必须动态创建大量控件。更改条件时,我需要删除所有动态创建的控件并进行重新计算,最后重新创建控件。
我通过首先在 TTabItem 之上动态创建一个 TScrollBox (iOFPLayout) 来处理这个问题。我通过更改其 parent 将预定义的 header 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 之前,为每个以 TScrollBox 作为父级的动态创建的控件调用 DisposeOf。
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;
我正在从事一个多平台项目,在该项目中,由于一些主要计算,我必须动态创建大量控件。更改条件时,我需要删除所有动态创建的控件并进行重新计算,最后重新创建控件。
我通过首先在 TTabItem 之上动态创建一个 TScrollBox (iOFPLayout) 来处理这个问题。我通过更改其 parent 将预定义的 header 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 之前,为每个以 TScrollBox 作为父级的动态创建的控件调用 DisposeOf。
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;