59

This question is a continuance of a particular comment from people on stackoverflow which I've seen a few different times now. I, along with the developer who taught me Delphi, in order to keep things safe, have always put a check if assigned() before freeing objects, and before doing other various things. However, I'm now told that I should not be adding this check. I'd like to know if there is any difference in how the application compiles/runs if I do this, or if it won't affect the result at all...

if assigned(SomeObject) then SomeObject.Free;

Let's say I have a form, and I'm creating a bitmap object in the background upon the form's creation, and freeing it when I'm done with it. Now I guess my problem is I got too used to putting this check on a lot of my code when I'm trying to access objects which might potentially have been free'd at some point. I've been using it even when it's not necessary. I like to be thorough...

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FBitmap: TBitmap;
  public
    function LoadBitmap(const Filename: String): Bool;
    property Bitmap: TBitmap read FBitmap;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FBitmap:= TBitmap.Create;
  LoadBitmap('C:\Some Sample Bitmap.bmp');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if assigned(FBitmap) then begin //<-----
    //Do some routine to close file
    FBitmap.Free;
  end;
end;

function TForm1.LoadBitmap(const Filename: String): Bool;
var
  EM: String;
  function CheckFile: Bool;
  begin
    Result:= False;
    //Check validity of file, return True if valid bitmap, etc.
  end;
begin
  Result:= False;
  EM:= '';
  if assigned(FBitmap) then begin //<-----
    if FileExists(Filename) then begin
      if CheckFile then begin
        try
          FBitmap.LoadFromFile(Filename);
        except
          on e: exception do begin
            EM:= EM + 'Failure loading bitmap: ' + e.Message + #10;
          end;
        end;
      end else begin
        EM:= EM + 'Specified file is not a valid bitmap.' + #10;
      end;
    end else begin
      EM:= EM + 'Specified filename does not exist.' + #10;
    end;
  end else begin
    EM:= EM + 'Bitmap object is not assigned.' + #10;
  end;
  if EM <> '' then begin
    raise Exception.Create('Failed to load bitmap: ' + #10 + EM);
  end;
end;

end.

Now let's say I'm introducing a new custom list object called TMyList of TMyListItem. For each item in this list, of course I have to create/free each item object. There's a few different ways of creating an item, as well as a few different ways of destroying an item (Add/Delete being the most common). I'm sure it's a very good practice to put this protection here...

procedure TMyList.Delete(const Index: Integer);
var
  I: TMyListItem;
begin
  if (Index >= 0) and (Index < FItems.Count) then begin
    I:= TMyListItem(FItems.Objects[Index]);
    if assigned(I) then begin //<-----
      if I <> nil then begin
        I.DoSomethingBeforeFreeing('Some Param');
        I.Free;
      end;
    end;
    FItems.Delete(Index);
  end else begin
    raise Exception.Create('My object index out of bounds ('+IntToStr(Index)+')');
  end;
end;

In many scenarios, at least I would hope that the object is still created before I try to free it. But you never know what slips might happen in the future where an object gets free'd before it's supposed to. I've always used this check, but now I'm being told I shouldn't, and I still don't understand why.


EDIT

Here's an example to try to explain to you why I have a habit of doing this:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SomeCreatedObject.Free;
  if SomeCreatedObject = nil then
    ShowMessage('Object is nil')
  else
    ShowMessage('Object is not nil');
end;

My point is that if SomeCreatedObject <> nil is not the same as if Assigned(SomeCreatedObject) because after freeing SomeCreatedObject, it does not evaluate to nil. So both checks should be necessary.

23
  • How does assigned(I) differ from I <> nil? (Note that I do not use Delphi at all :p~)
    – user166390
    Commented Dec 18, 2011 at 0:09
  • 16
    @pst, Assigned is exactly the same as <> nil in most cases. The exception is events, where Assigned has a bit of black magic to work around issues that could otherwise arise in the form designer (so you always need to use Assigned to check whether an event is assigned, whereas for anything else Assigned and <> nil are equivalent).
    – Joe White
    Commented Dec 18, 2011 at 0:14
  • 16
    No, they usually mean the same thing. The only difference is that if F is a function variable returning a pointer, Assigned(F) checks whether F itself is nil, whereas F <> nil checks F's result.
    – user743382
    Commented Dec 18, 2011 at 0:15
  • 1
    @JerryDodge, the example in your edit doesn't actually explain anything. What is it you're trying to do?
    – Joe White
    Commented Dec 18, 2011 at 0:33
  • 3
    @Jerry Dodge - Also consider using FreeAndNil() instead of Free. It will help you a lot!!!!
    – IceCold
    Commented Jun 19, 2013 at 16:50

5 Answers 5

140

This is a very broad question with many different angles.

The meaning of the Assigned function

Much of the code in your question betrays an incorrect understanding of the Assigned function. The documentation states this:

Tests for a nil (unassigned) pointer or procedural variable.

Use Assigned to determine whether the pointer or the procedure referenced by P is nil. P must be a variable reference of a pointer or procedural type.

Assigned(P) corresponds to the test P <> nil for a pointer variable, and @P <> nil for a procedural variable.

Assigned returns False if P is nil, True otherwise.

Tip: When testing object events and procedures for assignment, you cannot test for nil, and using Assigned is the right way.

....

Note: Assigned cannot detect a dangling pointer--that is, one that is not nil, but that no longer points to valid data.

The meaning of Assigned differs for pointer and procedural variables. In the rest of this answer we will consider pointer variables only, since that is the context of the question. Note that an object reference is implemented as a pointer variable.

The key points to take from the documentation are that, for pointer variables:

  1. Assigned is equivalent to testing <> nil.
  2. Assigned cannot detect whether the pointer or object reference is valid or not.

What this means in the context of this question is that

if obj<>nil

and

if Assigned(obj)

are completely interchangeable.

Testing Assigned before calling Free

The implementation of TObject.Free is very special.

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

This allows you to call Free on an object reference that is nil and doing so has no effect. For what it is worth, I am aware of no other place in the RTL/VCL where such a trick is used.

The reason why you would want to allow Free to be called on a nil object reference stems from the way constructors and destructors operate in Delphi.

When an exception is raised in a constructor, the destructor is called. This is done in order to deallocate any resources that were allocated in that part of the constructor that succeeded. If Free was not implemented as it is then destructors would have to look like this:

if obj1 <> nil then
  obj1.Free;
if obj2 <> nil then
  obj2.Free;
if obj3 <> nil then
  obj3.Free;
....

The next piece of the jigsaw is that Delphi constructors initialise the instance memory to zero. This means that any unassigned object reference fields are nil.

Put this all together and the destructor code now becomes

obj1.Free;
obj2.Free;
obj3.Free;
....

You should choose the latter option because it is much more readable.

There is one scenario where you need to test if the reference is assigned in a destructor. If you need to call any method on the object before destroying it then clearly you must guard against the possibility of it being nil. So this code would run the risk of an AV if it appeared in a destructor:

FSettings.Save;
FSettings.Free;

Instead you write

if Assigned(FSettings) then
begin
  FSettings.Save;
  FSettings.Free;
end;

Testing Assigned outside a destructor

You also talk about writing defensive code outside a destructor. For example:

constructor TMyObject.Create;
begin
  inherited;
  FSettings := TSettings.Create;
end;

destructor TMyObject.Destroy;
begin
  FSettings.Free;
  inherited;
end;

procedure TMyObject.Update;
begin
  if Assigned(FSettings) then
    FSettings.Update;
end;

In this situation there is again no need to test Assigned in TMyObject.Update. The reason being that you simply cannot call TMyObject.Update unless the constructor of TMyObject succeeded. And if the constructor of TMyObject succeeded then you know for sure that FSettings was assigned. So again you make your code much less readable and harder to maintain by putting in spurious calls to Assigned.

There is a scenario where you need to write if Assigned and that is where the existence of the object in question is optional. For example

constructor TMyObject.Create(UseLogging: Boolean);
begin
  inherited Create;
  if UseLogging then
    FLogger := TLogger.Create;
end;

destructor TMyObject.Destroy;
begin
  FLogger.Free;
  inherited;
end;

procedure TMyObject.FlushLog;
begin
  if Assigned(FLogger) then
    FLogger.Flush;
end;

In this scenario the class supports two modes of operation, with and without logging. The decision is taken at construction time and any methods which refer to the logging object must test for its existence.

This not uncommon form of code makes it even more important that you don't use spurious calls to Assigned for non-optional objects. When you see if Assigned(FLogger) in code that should be a clear indication to you that the class can operate normally with FLogger not in existence. If you spray gratuitous calls to Assigned around your code then you lose the ability to tell at a glance whether or not an object should always exist.

21
  • 3
    @David, in the TMyObject.Destroy you are calling FLogger.Free without checking if it is Assigned. is that because TMyObject.Create will always initialize it to nil when UseLogging is False? when declaring a local TObject variable in a procedure, we cannot simply call object.Free without first initializing it. or am I wrong?
    – kobik
    Commented Dec 18, 2011 at 12:56
  • 4
    @kobik You are 100% correct. An instance of a class is guaranteed to be zero initialized. Local variables are uninitialized. Commented Dec 18, 2011 at 13:10
  • 1
    @David Heffernan. Yes, I considered adding "which can be very confusing". But the point is that the answer is wrong: you can call the example .update on the example tmyobject or an object declared but not created as tmyobject, and since it tests IsAssigned, the operation of .update is defined, not "anyone's guess".
    – david
    Commented Dec 3, 2013 at 4:01
  • 1
    @David Heffernan Not that I want to bang on about it, but the answer still reads "you simply cannot call TMyObject.Update unless the constructor of TMyObject succeeded". I take your point that you assumed a declaration of FSettings that did not imply FSettings was NIL: since the question is about the use of IsAssigned, I made the opposite assumption.
    – david
    Commented Dec 5, 2013 at 2:04
  • 8
    David Heffernan is a living wikipedia :) Thanks for this anser
    – mca64
    Commented Sep 24, 2014 at 8:45
23

Free has some special logic: it checks to see whether Self is nil, and if so, it returns without doing anything -- so you can safely call X.Free even if X is nil. This is important when you're writing destructors -- David has more details in his answer.

You can look at the source code for Free to see how it works. I don't have the Delphi source handy, but it's something like this:

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

Or, if you prefer, you could think of it as the equivalent code using Assigned:

procedure TObject.Free;
begin
  if Assigned(Self) then
    Destroy;
end;

You can write your own methods that check for if Self <> nil, as long as they're static (i.e., not virtual or dynamic) instance methods (thanks to David Heffernan for the documentation link). But in the Delphi library, Free is the only method I know of that uses this trick.

So you don't need to check to see if the variable is Assigned before you call Free; it already does that for you. That's actually why the recommendation is to call Free rather than calling Destroy directly: if you called Destroy on a nil reference, you would get an access violation.

23
  • 3
    Unless you're working with a delegate type (procedure of object or function of object), Assigned is exactly the same as checking for <> nil.
    – Joe White
    Commented Dec 18, 2011 at 0:15
  • 4
    Well of course. If you do x.Free, then x still points to the memory address where the object used to be, and both x <> nil and Assigned(x) will return True. So if your variable isn't going out of scope right away, then it's a good habit to set it to nil when you free the object it's pointing to. That's why FreeAndNil was invented.
    – Joe White
    Commented Dec 18, 2011 at 0:23
  • 4
    What on earth did I say that you're twisting to mean "calling x.Free will change the x variable to point to nil"? I've clearly and specifically said the opposite.
    – Joe White
    Commented Dec 18, 2011 at 0:35
  • 4
    @JerryDodge: It sounds like you're somehow convinced that Assigned(I) has some magic ability to check whether I points to an object that's already been freed. It doesn't. Like we keep telling you, Assigned checks for nil. Try it. I := TObject.Create; I.Free; if I <> nil then ShowMessage('I <> nil'); if Assigned(I) then ShowMessage('Assigned(I)'); will show two messages: I <> nil and Assigned(I). That's because both checks do the exact same thing.
    – Joe White
    Commented Dec 18, 2011 at 1:39
  • 3
    @Jerry - Ctrl+Click on Assigned in Joe's test code, see if it takes you to system unit. Commented Dec 18, 2011 at 2:23
21

Why you shouldn't call

if Assigned(SomeObject) then 
  SomeObject.Free;

Simply because you would execute something like this

if Assigned(SomeObject) then 
  if Assigned(SomeObject) then 
    SomeObject.Destroy;

If you call just SomeObject.Free; then it's just

  if Assigned(SomeObject) then 
    SomeObject.Destroy;

To your update, if you afraid of the object instance reference use FreeAndNil. It will destroy and dereference your object

FreeAndNil(SomeObject);

It's similar as if you call

SomeObject.Free;
SomeObject := nil;
1
  • 5
    +10 for this!! The whole discussion under this topic comes down to the following point: SomeObject.Free destroys the instance by calling the destructor chain and releasing the allocated memory. It does not change the value of SomeObject. Since it is a pointer, i.e. a memory address, it will still bear that very same address after SomeObject.Free Moreover, AFAIK, the freed memory is not filled with null bytes as TObject.InitInstance does upon construction. So, ideally, use FreeAndNil( SomeObject ) otherwise there's no chance to tell living instance vars from dead ones
    – Semanino
    Commented Sep 27, 2017 at 11:15
0

I create a simple example showing my procedure:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Cir : TCircle;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Assigned(Cir) then
  begin
    Cir.Free;
    Cir := Nil;
  end;
  Cir := TCircle.Create(nil);
  with Cir do
  begin
    Parent := Form1;
    Height := 50;
    Width := 50;
    Position.X := 100;
    Position.Y := 100;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if Assigned(Cir) then
  begin
    Cir.Free;
    Cir := Nil;
  end;
end;

end.
1
  • How does this answer the question? Are you trying to ask a new question? Commented Apr 1, 2022 at 16:20
-4

I'm not completely sure of that, but it seems:

if assigned(object.owner) then object.free 

works fine. In this example it would be

if assigned(FBitmap.owner) then FBitmap.free
2
  • no it still doesn't work. I mean sometimes it works sometimes it doesn't... as above.
    – Mariusz980
    Commented Mar 16, 2016 at 17:00
  • but additionally testing "object <> nil" - also hasnt sorted out the problem... I prefer using another variable fex. "ifcreated_object : boolean". Well, it works fine.
    – Mariusz980
    Commented Mar 16, 2016 at 17:06

Not the answer you're looking for? Browse other questions tagged or ask your own question.