这个例子当然是简化的,但基本上我有一个主窗体,用
触发另一个窗体(frmSettings)function Execute(var aSettings: TSettings):Boolean
TSettings
是我自己创建的主要对象,用于跟踪设置。
在这个新打开的表单(frmSettings)中,我从TMyObjectList
获取了一个TObjectList
后代。
它充满了TMyObj
。
然后我使用该TMyObjectList中的值填充TListBox
。
代码:
...
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存储在设置类中的正确方法吗?
答案 0 :(得分:1)
这是在设置中存储所选对象的正确方法吗?
可能不是。您的设置类不应以任何方式依赖于表单。如果您决定在每次用户打开设置时动态创建和销毁表单,该怎么办?在这种情况下,您的设置将保留无效的对象引用。
恕我直言,最好将对象列表与所选对象的索引一起存储在设置中。在用户确认后,表单应该只访问设置,填充列表框并修改选定的对象索引。
您在代码中产生内存泄漏。您创建一个 TObjectList
作为本地变量,但您永远不会释放它。如果释放局部变量,列表框中的对象引用将无效。您有两种选择:
将对象列表存储为表单的成员变量,在FromCreate
事件处理程序中创建并在FormDestroy
事件处理程序中将其销毁。然后,您可以在列表框中安全地使用对象引用。
将对象列表存储在外面的某处,并将其作为 Execute
方法的参数传递给表单。在这种情况下,您还可以安全地使用对象引用。
答案 1 :(得分:0)
我会将myObjectList重命名为GlobalObjectList,并将其移出类。它可以在表单中声明,但在初始化/完成部分中创建/释放。在初始化期间,创建列表后,从ini文件(或存储它的任何位置)填充它。现在,您可以从使用单位的任何地方访问它。
答案 2 :(得分: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;
然后您的设置可以存储在文本文件或文本字符串中。
这只是一个解决方案。但是将设置存储为文本非常方便。我们在框架to store settings via a code-generated user interface中使用了这种方法。从TPersistent实例树创建设置树。