Delphi: правильный способ хранения объектов, извлеченных из TObjectList - PullRequest
3 голосов
/ 31 января 2011

Этот пример, конечно, упрощен, но в основном у меня есть основная форма, которая вызывает другую форму (frmSettings) с

function Execute(var aSettings: TSettings):Boolean

TSettings - это мой собственный объект, созданный в главной форме для отслеживанияsettings.

В этой недавно открытой форме (frmSettings) я получаю TMyObjectList, который является потомком TObjectList.Он заполняется TMyObj.

Затем я заполняю TListBox значениями из этого TMyObjectList.

код:

...

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 в классе настроек?

Ответы [ 3 ]

1 голос
/ 31 января 2011

Это правильный способ сохранить выбранный объект в настройках?

Вероятно, нет. Ваш класс настроек никак не должен зависеть от формы. Что если вы решите создавать и уничтожать форму динамически каждый раз, когда пользователь открывает настройки? В этом случае ваши настройки будут содержать недопустимую ссылку на объект.

ИМХО лучше сохранить список объектов в настройках вместе с индексом выбранного объекта. Форма должна просто получить доступ к настройкам, заполнить список и изменить индекс выбранного объекта после того, как пользователь подтвердил OK.

Вы вызываете утечку памяти в вашем коде. Вы создаете TObjectList как локальную переменную, но никогда не освобождаете ее. И если вы освободите локальную переменную, ссылки на объекты в списке будут недействительными. У вас есть два варианта:

  • Сохраните список объектов как переменную-член вашей формы, создайте в обработчике события FromCreate и уничтожьте его в обработчике события FormDestroy. Затем вы можете безопасно использовать ссылки на объекты в вашем списке.

  • Сохраните список объектов где-то снаружи и передайте его в форму в качестве параметра метода Execute. В этом случае вы также можете безопасно использовать ссылки на объекты.

0 голосов
/ 31 января 2011

А как насчет сериализации 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.

0 голосов
/ 31 января 2011

Я бы переименовал myObjectList в GlobalObjectList и переместил бы его из класса. Его можно объявить в форме, но создать / освободить в разделах инициализации / финализации. Во время инициализации, после создания списка, заполните его из INI-файла (или где бы вы его ни хранили). Теперь вы можете получить к нему доступ из любого места, где находится ваш юнит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...