开发者

which is the best way to control a object-field type life-cycle?

开发者 https://www.devze.com 2023-03-17 16:34 出处:网络
TMyClass = class(TObject) private FMyObject: TObject; function GetMyObject: TObject; public property MyObject: TObject read GetMy开发者_StackOverflowObject write FMyObject;
TMyClass = class(TObject)
private
  FMyObject: TObject;
  function GetMyObject: TObject;
public
  property MyObject: TObject read GetMy开发者_StackOverflowObject write FMyObject;
end;

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

  Result := FMyObject;
end;

Sometimes, "MyObject" is not created internally but externally created and assigned to the parameter. If this object is created externally, I can not free it in this context.

Should I create a TList and Add in all objects that were created internally and destroy everything on the destructor?

How can I control the lifetime of a parameter if it is created internally or not? What you suggest to do? Is there any pattern to do this?


I'd set a flag in the Property Setter

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;


I guess the best would be to redesign your code so that this problem won't arise - this kind of ownership ambiguity is a mess.

Anyway, one option would be to use (reference counted) interfaces. This is problematic in case of circular references.

If the externally created object must not be the only reference then you could still create internal copy of the object, something like

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

You could assign external object to different field than internal and then you don't Free that field in destructor. Or set a flag in the property setter so that you know not to free the external object...


The two most logical and practical solutions (keep a flag, copy on assignment) are already given, but for completeness sake and since the object field isn't likely to be of the TObject type, here are three other approaches. The practicality of these depends on the type of the object field, whether you really don't want an extra boolean flag and whether you dislike to add some intelligent behavior to this construction.

(Warning: this may be a little farfetched.)

  • Test if the object field is of your private object type:

        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;
    
  • Test on the ownership of the object field:

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

    This construction is especially useful if the external object should anyway be freed by this class: just set its Owner to this class instance. The decision depends no longer on the internal or external creation of the object.

  • If the object field descends from TComponent, then you do not have to free at all.

0

精彩评论

暂无评论...
验证码 换一张
取 消