views:

303

answers:

4

As there is no garbage collection in Delphi, where exactly do you unload variables?

Say I have a type with a set of private vars. Would it suffice to have a Destroy method that does this work? Do I need to explicitly call this destroy method in my consuming classes?

+10  A: 

The best way to organize destruction in delphi is to always think about "who will create the given vars".

If you also free them in this context (For you private vars the destroy method of the class) it's much less likely that you will encounter memory leaks.

Actually the destructor of a class is normally not called via

myInstance.Destroy();

instead the typical way of doing it is via

FreeAndNil(myInstance);

or

myInstance.Free();

As delphi will handle calling the destructor method at order

tDo
+1 if you follow "who creates, destroys", then you are already 98% there.
Marco van de Voort
But how far do you take this. If for example my form destroys my model class, does my model class still have to free all it's own variables? My current employer tells me the following line in my form's FormDestroy() suffices: fTheInstance.Destroy; The only XP I have with non-gb-collected was Cobol and Oberon and my instinct tells me that line is not enough..
borisCallens
about destroy, taken from "Delphi in a nutshell":http://oreilly.com/catalog/delphi/chapter/ch02.html...When freeing an object, you should call the Free method and not the destructor. The distinction is important, because Free checks whether the object reference is nil and calls Destroy only for non-nil references. In extraordinary circumstances, a class can redefine the Free method ... which makes it that much more important to call Free, not Destroy.If you just call destroy on your main object without destroying childrne explicily you will probably encounter memory leaks
tDo
+3  A: 

Objects need to be destroyed when you are ready with them: using .Free (which calls the destructor .Destroy) or FreeAndNil.

If you are using interfaces: they are reference counted and hence they are freed for you. The same for strings.

Most functions that you need to pass an object do not take ownership of the object so you should free it afterwards when you are done with them. If you create components on a form, please note that they are objects, are owned by the form and you should let the form free them for you.

For example: in your TLight you create a TTimer probably without an owner. In this case you should Free the timer in the destructor of the TLight class. If your TLight was a TControl or TComponent itself you could have created the Timer with Self as the Owner and it was automatically freed when your TLight instance was freed.

Ritsaert Hornstra
Re ownership - Note that this is not compiler magic but just plain old Delphi code buried in the VCL.
Ulrich Gerhardt
@Ulrich: Yes. I think ownership is a complex feat for someone coming from a garbage collected language: TList has no ownership of it's objects, TObjectList has, TComponents has a simple ownership buildin, but when a Parent of a control (also a component) is destoyed it will destroy it's children even when the Owner is set to a different component. A lot of bugs in Delphi, C, C++ originates in ownership issues and it even plagues sources from seasoned programmers.
Ritsaert Hornstra
Interfaces are a special case in my experience. If a class has a property of an interface type, I found it necessary to assign nil to the private field for this property in the destructor. Destroying the object will not decrement the reference counters. Maybe I am wrong but I'll test it again :)
mjustin
Mjustin, if you're using an interface, you shouldn't even *have* any object reference for you to destroy. If you have an interface property, then you should have an interface field, not an object field. The interface field will get cleared automatically after the final destructor has run (in FreeInstance).
Rob Kennedy
@mjustin: there should be no need to set reference counted items to nil (strings, interface instances, dynamic arrays). The finilization of the instance will do that for you already.
Ritsaert Hornstra
@mjustin: Are you using `TAggregatedObject`? You should, as this would stand things from their head to their feet - the destructor should be called only as an effect of the ref count being decremented to 0.
mghie
I have noticed the concept of owner. Yet I couldn't pass my custom class to the TTimer.Create() method as it is expecting a TComponent. Why do they restrict things to be used in forms only? Doesn't that furhter petrify the bad habit of putting logic in your views (forms)?
borisCallens
You can just call fTimer := TTimer.Create(nil). The TTimer isn't bound to TComponents. But you have to do the freeing yourself.
The_Fox
@Rob: there is no object refrence, only an interface field, assigned by code setting the property on the created instance. On destroy, the interface field is *not* cleared automatically. Example: `C := TMyContainer.Create; C.Int := TMyComponent.Create(nil); C.Free;` -> FInt will leak memory, if the class implementing the interface is a subclass of TComponent.
mjustin
@MJustin: it shouldn't leak, unless TMyComponent is a class (like TComponent) where the AddRef en ReleaseRef methods are overriden to prevent reference counting. Or your interface has a circular reference. Anyway, you've probably created a bug :)
The_Fox
@The_Fox: see http://stackoverflow.com/questions/2182612/ for a full example. I created a bug and forgot to call Bug.Free :) ?
mjustin
Oh, well, that's an entirely different problem, @Mjustin. Nothing to do with interface properties at all. The field *is* cleared. You can set a breakpoint in FreeInstance or _Release to see for yourself. But if the object is a component, it doesn't free itself. That's because TComponent is kind of a weird implementor or IUnknown; it doesn't count its own references. Something to do with the VCLComObject property, something I've never had need to use nor motivation to learn.
Rob Kennedy
@MJustin: Like I said, TComponent has a different implementation of AddRef and ReleaseRef and therefore cannot be reference counted. So yes, you forgot to call Bug.Free :P
The_Fox
+1  A: 

You keep referring to a "type with private vars" and asking about "unloading variables." I'm going to go out on a limb (as I may be have misinterpreted your message), and presume you're talking about class variables and are asking about when these class variables should be finalized when the "class unloads." (You've been doing Java or .NET programming at some point, no ;-).

The key to what you're asking is a unit finalization section. The overall layout of a unit includes 4 overall sections, two of which are optional, initialization and finalization.

unit Foo;
interface
  // put things here you want other units to see
implementation
  // put the *implementation* of the things from the interface here along with things
  // you only want this unit to see.
initialization
  // <optional> put code here that you want to execute when this unit is "loaded"
finalization
  // <optional> put code here that you want to execute when this unit is "unloaded"
end.

If you're talking about instance variables of a class type, then some of the other answers you've already gotten (and are bound to get) should shed some light on that.

Allen Bauer
Thanks. Although this isn't really what I'm looking for, this simple overview clears up some fog that was still hanging around. Yes I'm from a C# bgnd, but it never hurts to try a different language to "sharpen the saw", right? ;) I like the init and finit sections. Think I might use them.
borisCallens
+3  A: 

That's how I see it:

Primitive types (like integer, real, double, extended), arrays, sets, records: they are automatically destroyed when out of scope.

Reference-counted types (strings, interfaces types, interface references): they are automatically destroyed when their reference counter is equal to zero.

Objects from classes that fall in one of those situations: does not descend from TComponent, are in list objects that do not implement ownership or are a TComponent descendant but does not have an owner assigned to it:

  • When their class descend from TInterfacesObject and are accessed from a interface reference: same behavior from reference-counter types (see above)
  • Other cases: call Object.Free or FreeAndNil(Object) to manually destroy it (in a try..finally structure would be even better)

Objects that have an owner (TComponent descendants or are in list objects that implement ownership): the owner will take of them in proper time.

I noticed the following comment from OP:

I have noticed the concept of owner. Yet I couldn't pass my custom class to the TTimer.Create() method as it is expecting a TComponent. Why do they restrict things to be used in forms only? Doesn't that furhter petrify the bad habit of putting logic in your views (forms)?

See, a TComponent descendant can be registered on the pallete to have the ability to be used in design-time (for example, in a form) . But it's doesn't means it must!! You can pass your custom-class to TTimer.Create() if it descends from TComponent but you don't need to instantiate it in design-time - or even be owned by a TForm descendant (if you want to manage it's lifetime, you don't even need an owner!).

Ownership is a mechanism it's to help people to save time....

Fabricio Araujo
Thanks that's understandable language for me :)
borisCallens
Wish I could mark two answers as this answer says when to destroy what and the other says how to do it
borisCallens