sartoz
sartoz

Reputation: 330

Check free object instance

I have the following class structure.

TObjectA = class
    ...
end;

TObjectB = class
    private
        FDependencyObjectA
    public
        constructor Create(const DependencyA: TObjectA);
        destructor Destroy; override;
end;

{ TObjectB }

constructor TObjectB.Create(const DependencyA: TObjectA);
begin
    FDependencyObjectA := DependencyA;
end;

destructor TObjectB.Destroy;
begin
  FDependencyObjectA.Free;
  FDependencyObjectA := nil; 
  inherited;
end;

I'm trying to find a solution to work with these two use cases.

// Use cases

// First case 
// Works with Free on Destroyer
// Does not work without Free on Destroyer
procedure TForm1.Button1Click(Sender: TObject);
var
 ObjectB: TObjectB;
begin
   ObjectB := TObjectB.Create(TObjectA.Create);
   try
       ...
   finally
       ObjectB.Free; 
   end;
end;


// Second case
// Does not work with Free on Destroyer
// Works without Free on Destroyer
procedure TForm1.Button1Click(Sender: TObject);
var
 ObjectA: TObjectA;
 ObjectB: TObjectB;
begin
   ObjectA := TObjectA.Create;
   ObjectB := TObjectB.Create(ObjectA);
   try
       ...
   finally
       // Depending on the implementation of the Destroyer or the this finally
       // Can raise Access Violation, Invalid Pointer or Memory Leak
       ObjectB.Free; 
       ObjectA.Free;
   end;
end;

One solution would be to verify that ObjectA has already been free of memory. But I do not know a check only when the object is free, I know checks of when the object is null.

// Third case (Trying for a solution)
procedure TForm1.Button1Click(Sender: TObject);
var
 ObjectA: TObjectA;
 ObjectB: TObjectB;
begin
   ObjectA := TObjectA.Create;
   ObjectB := TObjectB.Create(ObjectA);
   try
       ...
   finally
       ObjectB.Free;       
       // It would work fine if Assigned returned false, but always returns true
       if Assigned(ObjectA) then  // or ObjectA <> nil
         ObjectA.Free;
         // If I use just assign nil instead of Free, compile throws a hint
         // ObjectA := nil;
         // H2077 Value assigned to 'Your_Variable' never used
   end;
end;

Another solution would be to inject the dependency using the reserved word var. But I want to evaluate other possibilities first.

In Delphi is there any way to check if the object is just free and not only nil?

Or is there any workaround to work with the first two use cases, without Access Violation, Invalid Pointer, Memory Leak or Compile Hints errors?

I did not just want to adopt just one use case, or have to keep checking the object's Destroyer to know how to implement a new function / procedure. This is bad for day-to-day implementations, or for when a new developer gets into the project. We would have to explain all these little rules and also be constantly reviewing whether the implementations are correct.

Edit:

I know that Delphi has ARC for Interfaces, but not all objects will implement an interface.

For hints there is the {$ Hints Off} compilation directive, but in any case adding the directive is not very viable.

Upvotes: 0

Views: 468

Answers (1)

David Heffernan
David Heffernan

Reputation: 613461

The correct code is this:

ObjectA := TObjectA.Create;
ObjectB := TObjectB.Create(ObjectA);
try
  ...
finally
  ObjectB.Free;       
end;

ObjectB takes ownership of ObjectA and it is therefore its job to destroy it. The calling code has passed on that responsibility and so has nothing more to do.

Upvotes: 1

Related Questions