Reputation: 330
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
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