3

这个例子当然是简化的,但基本上我有一个触发另一个表单(frmSettings)的主表单

function Execute(var aSettings: TSettings):Boolean

TSettings是我自己以主要形式创建的对象,用于跟踪设置。

在这个新打开的表单(frmSettings)中,我获取了TMyObjectList一个来自TObjectList. 它充满了TMyObj

TListBox然后我用该 TMyObjectList 中的值填充 a 。

编码:

...

FMyObjectList : TMyObjectList;
property MyObjectList: TMyObjectList read getMyObjectList;

...

function TfrmSettings.getMyObjectList: TMyObjectList ;
begin
    If not Assigned(FMyObjectList) then FMyObjectList := TMyObjectList.Create(True)
    Result := FMyObjectList;
end;

function TfrmSettings.Execute(var aSettings: TSettings): Boolean;
begin

    //Fill myObjectList
    FetchObjs(myObjectList);

    //Show list to user
    FillList(ListBox1, myObjectList);

    //Show form        
    ShowModal;

    Result := self.ModalResult = mrOk;

    if Result then
    begin
        // Save the selected object, but how??

        // Store only pointer? Lost if list is destroyed.. no good
        //Settings.selectedObj := myObjectList.Items[ListBox1.ItemIndex];

        // Or store a new object? Have to check if exist already?
        If not Assigned(Settings.selectedObj) then Settings.selectedObj := TMyObj.Create;
        Settings.selectedObj.Assign(myObjectList.Items[ListBox1.ItemIndex];);
    end;

end;

procedure TfrmSettings.FillList(listBox: TListBox; myObjectList: TMyObjectList);
var
    i: Integer;
begin
    listBox.Clear;
    With myObjectList do
    begin
        for i := 0 to Count - 1 do
        begin
            //list names to user
            listBox.Items.Add(Items[i].Name);
        end;
    end;
end;

procedure TfrmSettings.FormDestroy(Sender: TObject);
begin
    FreeAndNil(FMyObjectList);
end;

只存储指针似乎不是一个好主意,因为再次触发设置表单,重新创建列表,即使用户点击“取消”,原始对象也会丢失

所以存储一个副本似乎更好,使用 assign 来获得正确的所有属性。首先检查我是否已经有一个对象。

        If not Assigned(Settings.selectedObj) then Settings.selectedObj := TMyObj.Create;
        Settings.selectedObj.Assign(myObjectList.Items[ListBox1.ItemIndex];);

我应该将这两行移到一个方法而不是像Settings.AssignSelectedObj(aMyObj:TMyObj)

这看起来正确还是我以错误的方式实施?更多/更少需要的东西?

我需要一些指导方针,这样我就不会因为内存泄漏和其他麻烦而感到更加安全。

除了稍微查看代码之外,真正的问题是:这是将我的 SelectedObject 存储在设置类中的正确方法吗?

4

3 回答 3

1

这是将所选对象存储在设置中的正确方法吗?

可能不是。您的设置类不应以任何方式依赖于表单。如果您决定在每次用户打开设置时动态创建和销毁表单怎么办?在这种情况下,您的设置将包含无效的对象引用。

恕我直言,最好将对象列表与所选对象的索引一起存储在设置中。用户点击 OK 确认后,表单应该只访问设置、填写列表框并修改所选对象索引。

您正在代码中产生内存泄漏。您将 a 创建TObjectList为局部变量,但从不释放它。如果释放局部变量,列表框中的对象引用将无效。你有两个选择:

  • 将对象列表存储为表单的成员变量,在事件处理程序中创建并在FromCreate事件处理程序中销毁它FormDestroy。然后,您可以安全地在列表框中使用对象引用。

  • 将对象列表存储在外部某处,并将其作为方法的参数传递到表单中Execute。在这种情况下,您还可以安全地使用对象引用。

于 2011-01-31T14:15:44.333 回答
0

我会将 myObjectList 重命名为 GlobalObjectList,并将其移出班级。它可以在表单中声明,但在初始化/终结部分中创建/释放。在初始化期间,创建列表后,从 ini 文件(或存储它的任何位置)填充它。现在您可以从任何有您的单位在使用中的地方访问它。

于 2011-01-31T14:17:31.470 回答
0

TSettings的序列化呢?将您的设置放入一些已发布的属性中,然后让 RTTI 保存其内容:

type
  TSettings = class(TPersistent)
  public
    function SaveAsText: UTF8String;
  end;

function TSettings.SaveAsText: UTF8String;
begin
var
  Stream1, Stream2: TMemoryStream;
begin
  Stream1 := TMemoryStream.Create;
  Stream2 := TMemoryStream.Create;
  try
    Stream1.WriteComponent(MyComponent);
    ObjectBinaryToText(Stream1, Stream2);
    SetString(result,PAnsiChar(Stream2.Memory),Stream2.Size);
  finally
    Stream1.Free;
    Stream2.Free;
  end;
end;

然后您的设置可以存储在文本文件或文本字符串中。

这只是一种解决方案。但是将设置存储为文本非常方便。我们在我们的框架中使用这种方法,通过代码生成的用户界面存储设置。从 TPersistent 实例树创建设置树。

于 2011-01-31T16:16:35.877 回答