Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why FreeAndNil implementation doing Nil before Free?

If you will look at the code of FreeAndNil procedure you will see:

procedure FreeAndNil(var Obj);
var
  Temp: TObject;
begin
  Temp := TObject(Obj);
  Pointer(Obj) := nil;
  Temp.Free;
end;

What is the reason they assigning Nil to an object reference and only after this destroying it? Why not vice-versa?

like image 846
Andrew Avatar asked Oct 29 '14 06:10

Andrew


Video Answer


2 Answers

I can think of two reasons for doing it this way round, neither of which seems at all compelling.

Reason 1: to guarantee that the reference is set to nil in case an exception is raised

The implementation achieves this. If the destructor raises, then the reference is still set to nil. Another way to do so would be with a finally block:

try
  TObject(Obj).Free;
finally
  TObject(Obj) := nil;
end;

The downside of this is performance. Particularly on x86 a try/finally is a little expensive. In such a fundamental routine it is prudent to avoid this expense.

Why do I find the desire to nil at all costs not to be compelling? Well, as soon as destructor start failing you may as well give up. You can no longer reason clearly about your program's state. You cannot tell what failed and what state your program is in. It is my view that the correct course of action in the face of a destructor that raises is to terminate the process.

Reason 2: to ensure that other threads can detect that the object is being destroyed

Again this is achieved but it is of no practical use. Yes you can test whether the reference is assigned or not. But then what? The other thread cannot call methods on the object without synchronization. All you could do is learn whether or not the object is alive. And if that is so, why would it matter if this status changed before or after the destructor runs?

So whilst I present this as a possible reason I cannot believe that anyone in Embarcadero was really swayed by this argument.

like image 196
David Heffernan Avatar answered Dec 04 '22 05:12

David Heffernan


There's a variation on David's second reason that is a little more compelling. Although one might argue that if it applies there are other problems that should be fixed.

Reason 3: to ensure event handlers on the same thread can detect that the object is being destroyed

Here's a concocted hypothetical example:

TOwner.HandleCallback;
begin
  if Assigned(FChild) then
    FChild.DoSomething;
end;

TChildClass.Destroy;
begin
  if Assigned(FOnCallback) then FOnCallback;
  inherited Destroy;
end;

Now if TOwner calls:

FChild.Free;
FChild := nil;

FChild will be asked to DoSomething in the middle of its destruction cycle. A certain recipe for disaster. The implementation of FreeAndNil neatly avoids this.

Yes you may argue that firing callback events during destruction is dangerous, but it does have its benefits. There are quite few examples in Delphi/VCL code. Especially if you expand the scope of concern to include calling polymorphic methods - which also put aspects of the destruction sequence outside of your control.

Now I must point out that I'm not aware of any specifc cases in Delphi library code where this problem could manifest. But there are some complex circular dependencies in parts of the VCL. So I wouldn't be surprised if changing the implementation to the more obvious choice in SysUtils leads to a few unpleasant surprises.

like image 21
Disillusioned Avatar answered Dec 04 '22 05:12

Disillusioned