Каков наилучший способ управления жизненным циклом типа поля объекта? - PullRequest
5 голосов
/ 04 июля 2011
TMyClass = class(TObject)
private
  FMyObject: TObject;
  function GetMyObject: TObject;
public
  property MyObject: TObject read GetMyObject write FMyObject;
end;

function TMyClass.GetMyObject: TObject;
begin
  if FMyObject = nil then
    FMyObject := TObject.Create;

  Result := FMyObject;
end;

Иногда «MyObject» создается не внутренне, а извне и присваивается параметру. Если этот объект создан извне, я не могу освободить его в этом контексте.

Должен ли я создать TList и добавить все объекты, которые были созданы внутри, и уничтожить все на деструкторе?

Как я могу контролировать время жизни параметра, если он создан внутри или нет? Что вы предлагаете делать? Есть ли способ сделать это?

Ответы [ 3 ]

7 голосов
/ 04 июля 2011

Я бы установил флаг в Настройщике свойств

procedure TMyClass.SetMyObject(AObject: TObject);
begin
  if Assigned(MyObject) and FIsMyObject then
    FMyObject.Free;
  FIsMyObject := False;
  FMyObject := AObject;
end;

function TMyClass.GetMyObject: TObject;
begin
  if FMyObject = nil then
  begin
    FMyObject := TObject.Create;
    FIsMyObject := True;
  end;

  Result := FMyObject;
end;

Destructor TMyClass.Destroy;
begin
    if FIsMyObject then
        FMyObject.Free;
end;
3 голосов
/ 04 июля 2011

Полагаю, лучше всего было бы переделать ваш код, чтобы эта проблема не возникала - такая неоднозначность в отношении владения - беспорядок.

В любом случае, одним из вариантов будет использование (подсчитанных) интерфейсов. Это проблематично в случае циклических ссылок.

Если внешне созданный объект не должен быть единственной ссылкой, вы все равно можете создать внутреннюю копию объекта, что-то вроде

procedure TMyClass.SetMyObject(const Value: TObject);
begin
   MyObject.Assign(Value);
end;

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

1 голос
/ 04 июля 2011

Два наиболее логичных и практичных решения (сохранить флаг, копировать при назначении) уже даны, но для полноты и поскольку поле объекта, скорее всего, не относится к типу TObject, вот три других подхода. Их практичность зависит от типа поля объекта, от того, действительно ли вам не нужен дополнительный логический флаг, и от того, хотите ли вы добавить немного интеллектуального поведения в эту конструкцию.

(Предупреждение: это может быть немного надуманным.)

  • Проверьте, относится ли поле объекта к типу вашего частного объекта:

        property MyObject: TSomeAncestor read GetMyObject write SetMyObject;
      end;
    
    implementation
    
    type
      TMyObject = class(TSomeAncestor) ... end;
    
    destructor TMyClass.Destroy;
    begin
      if FMyObject is TMyObject then
        FMyObject.Free;
    
  • Проверка владения полем объекта:

        property MyObject: TOwnedObject read GetMyObject write SetMyObject;
      end;
    
    implementation
    
    destructor TMyClass.Destroy;
    begin
      if FMyObject.Owner = Self then
        FMyObject.Free;
    

    Эта конструкция особенно полезна, если внешний класс все равно должен быть освобожден этим классом: просто установите для его Owner этот экземпляр класса. Решение больше не зависит от внутреннего или внешнего создания объекта.

  • Если поле объекта происходит от TComponent, вам вообще не нужно его освобождать.

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