Why FreeAndNil implementation doing Nil before Free?

笑着哭i 提交于 2020-06-27 07:00:15

问题


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?


回答1:


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.




回答2:


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.




回答3:


The only thing that really bothered me for years is that FreeAndNil(var obj) lacks type safety. I know that due to the lack of adequate language support there was simply no way to do it right, but since we have generics for a while, here's a quick and simple tool that can make your life easier.

type
  TypeSafe = class sealed
  public
    class procedure FreeAndNil<T : class>( var obj : T);  static; inline;
  end;


class procedure TypeSafe.FreeAndNil<T>( var obj : T);
begin
  SysUtils.FreeAndNil( obj);
end;

If you add an overload for the normal FreeAndNil(var Obj) and mark it as deprecatedyou got a fair chance to find all the places where someone hands over an interface pointer to it -- and if the code base is only large enough you will find interesting things, believe me.

procedure FreeAndNil(var Obj); inline; deprecated 'use TypeSafe.FreeAndNil<T>() instead';

Note that you don't even have to specify the <T>since the compiler is smart enough to find out the right type for you. Just add the unit on top and change all

FreeAndNil(foobar);

in your source code into

TypeSafe.FreeAndNil(foobar);

and you're done.



来源:https://stackoverflow.com/questions/26623852/why-freeandnil-implementation-doing-nil-before-free

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!