Hello, I don't have much history with Delphi. I like FireMonkey and program in C++ Builder, but recently I've tried Delphi since RAD Studio seems to work much better with Delphi.

Hello, I don't have much history with Delphi.  I like FireMonkey and program in C++ Builder, but recently I've tried Delphi since RAD Studio seems to work much better with Delphi.

My questions are:

1) How do you know when to call 'Free' on an object?  I know you should whenever you call 'Create', but what about when other methods allocate memory for you and pass you the new reference?  Is this just like in C++ when you can't be sure the function you are calling allocates memory unless it's documented?  Is there a convention for naming methods that allocate memory?

2) If I'm targeting a platform that has ARC in Delphi, what do I do about 'Create' and 'Free' then?  Does anything change from a Delphi developer's point of view?

Thanks in advance!

Comments

  1. If you Created it you should Free it as well. Free is ignored on mobile but in order to support cross platform just keep using Create and Free like it was Windows. If you REALLY want to free something you can call DisposeOf instead of Free (this is only needed in very few places and if you want to free the memory right now). This covers the Free/DisposeOf thing: http://docwiki.embarcadero.com/RADStudio/XE8/en/Automatic_Reference_Counting_in_Delphi_Mobile_Compilers Also see: http://www.delphibasics.co.uk/Article.asp?Name=Memory and http://www.delphibasics.co.uk/RTL.asp?Name=FreeAndNil

    ReplyDelete
  2. Usually the class that created the object owns it, i.e. keeps a reference to it and is responsible for freeing it. When this is not the case, it should be clear from the comments who owns it (is responsible for freeing the object).

    ReplyDelete
  3. On the desktop compilers the rule is simple. Every instance that is created must be destroyed.

    ReplyDelete
  4. Nah, it's not that easy. In most cases you need to free all objects on your own and usually an object creates and destroys other objects by itself. But when you have an object referenced by an interface and the object behind that interface is derived from TInterfacedObject then it is reference counted and freed automatically when there is no reference to it via one of the implemented interface types. In this case you are not allowed to destroy them on your own. Otherwise you will receive access violations. When two interfaced objects reference each other they will both never be freed until one of them sets their reference to the other object to nil.

    A similar case is when you work with TComponents and you set the owner. When the owner of a TComponent is set then  the owner is responsible for freeing it's children. It will crash when you destroy them on your own.

    Memory management is tricky in Delphi.

    ReplyDelete
  5. It really is that easy. Every instance that is created must be destroyed.

     - Some instances are owned by their interface references and will destroy them.
     - Some instances are owned by TComponent ownership, and are destroyed that way.
     - Some instances are owned by their owning instance and are destroyed explicitly in the owning instances destructor.
     - Some instances are local and owned by the procedure that creates them, and the code in that procedure must also destroy them.

    And so on.  Every time you create an object you need to be clear as to who owns it and who is responsible for destroying it.

    Every instance that is created must be destroyed by something. So every time you do 

        obj := TMyClass.Create;

    there must at some point be a call that does

        obj.Free;

    It's really not that hard.

    Personally I find ARC more difficult.

    ReplyDelete
  6. David Heffernan There are also reference counted objects in desktop compiler, that can make things look complicated. But rules there are also simple.
    Always use interface references for reference counted object instances and don't mix their usage with object references. Such objects will be automatically released by reference counting mechanism when last reference goes out of scope.

    ReplyDelete
  7. Christopher Wosinski If you have TComponent descendant with Owner set, then you can either leave its destruction to Owner, or you can free it yourself.

    TComponent has FreeNotification mechanism that will notify other components (including its owner) that component is about to be destroyed.

    Of course, if you don't use that mechanism properly then you may have crashes.

    Above is true for desktop compilers. Mobile with ARC is whole new ball game.

    ReplyDelete
  8. David Heffernan Dang. And I have read your post twice, because I thought it is impossible that you would forget to mention reference counted objects. I need to shop for new brain, this one is not working properly.

    ReplyDelete
  9. Thanks everyone for your help!  I think I'm understanding it, however, what are some examples of reference counted objects?  How can you tell they are reference counted?  Is that information in the documentation, or do all reference counted objects descend from 'TInterfacedObject'?  With these objects, I would only reference them via their interface and not call 'Free' on them, is that correct?

    Thanks again!

    ReplyDelete
  10. Michael Taylor Usually such information should be in documentation for the class you are using. If you are dealing with reference counted objects, like you said, you should only reference them through interface and not call Free.

    In general reference counted classes inherit from TInterfacedObject, but that is not strict rule, as any class can implement base IInterface at any point, so if you don't have proper documentation, you have to look at _Release method implementation for specific class. If the code calls Destroy when its reference count reaches 0 then you are dealing with reference counted class. 

    function TInterfacedObject._Release: Integer;
    begin
      Result := AtomicDecrement(FRefCount);
      if Result = 0 then Destroy;
    end;

    On the other hand you can have class that implements interfaces, but is not reference counted at all or only under some circumstances. Typical example is TComponent class that has reference counting disabled (unless you have component that supports COM). If reference counting is disabled you can store such object in normal object references and you have to Free it at some point, just like regular object (same rules apply).  However you can also safely pass such object around to methods that expect interface reference to be passed in (if the class implements expected interface).

    The only thing you have to be careful when dealing with such objects is that at point when object is being released you don't have any other references pointing to it, or you will get AV when that other reference goes out of scope. At that point _Release method will be called on already released object.

    function TComponent._Release: Integer;
    begin
      if FVCLComObject = nil then
        Result := -1   // -1 indicates no reference counting is taking place
      else
        Result := IVCLComObject(FVCLComObject)._Release;
    end;

    While above examples for detecting reference counted classes cover most scenarios, there is always possibility that some class implements some convoluted reference counting rules, but I would expect that such classes have their behavior fully documented.

    I hope I didn't forget anything important :)

    ReplyDelete

Post a Comment