Clear a TList or a TObjectList - delphi

I'm a bit puzzled of what to use for storing objects in a list.
Until now I have used TList and freed each item in a loop. Then I discovered TObjectList that do this automatically from Free. Then I saw this from the doc of TList.Clear:
Call Clear to empty the Items array and set the Count to 0. Clear also
frees the memory used to store the Items array and sets the Capacity to 0.
So it is basically the same. So
for TList
mylist.Clear;
myList.Free;
is the same as for TObjectList?
myList.Free;
Can TObjectList only be used for items as classes or can I store records?

1. TList won't free the elements, with both Clear or Free.
aList.Clear;
Will just set aList.Count := 0 without freeing the aList.Items[] elements. So you'll leak memory. You'll need an explicit free as such:
for i := 0 to aList.Count-1 do
TObject(aList[i]).Free;
But this is what TObjectList does... :)
About TObjectList, it is worth saying that TObjectList.Destroy is calling Clear.
So
aObjectList.Clear;
aObjectList.Free;
is exactly the same as
aObjectList.Free;
2. To store a list of records, you can use a dynamic array.
You'll get all TList methods (and more) with our dynamic array wrapper. That is, Add / Delete / Clear / Count / IndexOf / Find...
It has built-in serialization features (in binary or JSON), automated sorting and comparison (using RTTI) which are not existing with a TList/TObjectList. From Delphi 5 and later.
With more modern version of Delphi, you may use generics to handle the dynamic array, if you do not want to use a third-party library.

It's not the same TList.Clear only frees the memory allocated to store the pointers, not objects they are pointing to.
To avoid memory leaks you need to free the memory yourself - as you have been doing - or use TObjectList.
To answer the second question, TObjectList doesn't support storing records. You need to use TList (or something else) in that case.

Read what the documentation is saying more carefully:
Clear also frees the memory used to store the Items array
Only the memory for the array itself is freed, not the memory used by individual elements inside the array.

If you are using a recent version of Delphi I suggest that you use a generic list.
Right now you probably need to do a lot of casting when you use the objects from the list. With a generic list you don't have to do that anymore.
For instance if you have:
TMyObject = class(TObject);
Then you make the list like this:
TMyObjectList = TObjectList<TMyObject>;
There is an article in the Embarcadero Wiki:
http://docwiki.embarcadero.com/CodeExamples/XE8/en/Generics_Collections_TObjectList_(Delphi)

Really watching the TObjectList code in contnrs.pas OwnObjects does nothing. FOwnObjects is a variable declared in the private part of TObjectList that is not used anywhere in the TObjectList code (except just assigning the value), and there is no overridden Clear procedure in it.
So I'm afraid it necessary to free the memory just the same as in TList.

Related

Delphi "Free" TStringDynArray

Is there no need to free a TStringDynArray when created e.g. by SplitString? Attempts to free it fail as it is no object.
As I use it in a background process I am afraid that I create memory leaks by using it without explicitely freeing the memory.
No, a dynamic array is managed by the compiler. It is reference counted and will be freed when the reference count drops to zero.
(However, if the elements of the array are (pointers to) objects, these objects will not be freed automatically. Only the array itself is freed. In your case, the elements are strings, and they are also managed by the compiler.)
But you may occasionally want to free the memory before the variables go out of scope. For instance, if you have a global variable which is a huge dynamic array, you can explicitly do a SetLength(MyArray, 0) or MyArray := nil or Finalize(MyArray) to let go of it.

Is a dynamic array automatically deallocated when length is decreased?

I alread know, that a dynamic array is automatically deallocated/freed after use.
Does the same applies for resizing, especially decreasing? The manual and most help sites only cover increasing the array size.
test: array of TLabel;
SetLength(test, 10);
// fill array here
SetLength(test, 2); // <=== are entries 3-10 are automatically destroyed?
are entries 3-10 are automatically destroyed?
No, they are not automatically destroyed because those entries are dynamically allocated (and are not managed types). Only the pointers that refer to those items are released. It is your responsibility to destroy the items if necessary, because the compiler has no way to guarantee you wouldn't still use them from another reference (or have already destroyed them).
I must also point out that technically items "3-10" is wrong. Dynamic array are zero based. So the references for entries 2 to 9 are the ones released.
I alread know, that a dynamic array is automatically deallocated/freed after use
In addition, your question indicates you don't properly understand this. It seems you believed that when your array goes out of scope the labels referenced would be automatically destroyed. This is incorrect!
No matter where how or why some/all dynamic array entries are released Delphi won't automatically destroy objects types or any dynamically allocated pointer memory. Delphi only automatically releases memory for primitives (Integer, TDateTime, Double short strings), records and managed types1 (interfaces, long strings, other dynamic arrays).
1 Of course this is via reference counting. I.e. reference is reduced by 1; and the underlying object/string/array is released if and only if refCount is reduced to zero.
As whosrdaddy pointed out, if you want automatic destruction of contained objects, then you need to use a container that implements an ownership concept. TObjectList is an example. Although it doesn't work exactly like a dynamic array, it's behaviour is similar enough that it can usually be used as a replacement very easily.

Understanding memory allocation for TList<RecordType>

I have to store a TList of something that can easily be implemented as a record in Delphi (five simple fields). However, it's not clear to me what happens when I do TList<TMyRecordType>.Add(R).
Since R is a local variable in the procedure in which I create the my TList, I assume that the memory for it will be released when the function returns. Does this leave an invalid record pointer in the list? Or does the list know to copy-on-assign? If the former, I assume I would have to manually manager the memory for R with New() and Dispose(), is that correct?
Alternatively, I can "promote" my record type to a class type by simply declaring the fields public (without even bothering with making them formal properties). Is that considered OK, or ought I to take the time to build out the class with private fields and public properties?
Simplified: records are blobs of data and are passed around by value - i.e. by copying them - by default. TList<T> stores values in an array of type T. So, TList<TMyRecordType>.Add(R) will copy the value R into the array at position Count, and increment the Count by one. No need to worry about allocation or deallocation of memory.
More complex issues that you usually don't need to worry about: if your record contains fields of a string type, an interface type, a dynamic array, or a record which itself contains fields of one of these types, then it's not just a simply copy of data; instead, CopyRecord from System.pas is used, which ensures that reference counts are updated correctly. But usually you don't need to worry about this detail unless you are using Move to shift the bits around yourself, or doing similar low-level operations.

What happens when increasing the size of a dynamic array?

I'd like to understand what happens when the size of a dynamic array is increased.
My understanding so far:
Existing array elements will remain unchanged.
New array elements are initialised to 0
All array elements are contiguous in memory.
When the array size is increased, will the extra memory be tacked onto the existing memory block, or will the existing elements be copied to a entirely new memory block?
Does changing the size of a dynamic array have consequences for pointers referencing existing array elements?
Thanks,
[edit] Incorrect assumption struck out. (New array elements are initialised to 0)
Existing array elements will remain unchanged: yes
New array elements are initialized to 0: yes (see update) no, unless it is an array of compiler managed types such as string, an other array or a variant
All array elements are contiguous in memory: yes
When the array size is increased, the array will be copied. From the doc:
...memory for a dynamic array is reallocated when you assign a value to the array or pass it to the SetLength procedure.
So yes, increasing the size of a dynamic array does have consequences for pointers referencing existing array elements.
If you want to keep references to existing elements, use their index in the array (0-based).
Update
Comments by Rob and David prompted me to check the initialization of dynamic arrays in Delphi5 (as I have that readily available anyway). First using some code to create various types of dynamic arrays and inspecting them in the debugger. They were all properly initialized, but that could still have been a result prior initialization of the memory location where they were allocated. So checked the RTL. It turns out D5 already has the FillChar statement in the DynArraySetLength method that Rob pointed to:
// Set the new memory to all zero bits
FillChar((PChar(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);
In practice Embarcadero will always zero initialise new elements simply because to do otherwise would break so much code.
In fact it's a shame that they don't officially guarantee the zero allocation because it is so useful. The point is that often at the call site when writing SetLength you don't know whether you are growing or shrinking the array. But the implementation of SetLength does know – clearly it has to. So it really makes sense to have a well-defined action on any new elements.
What's more, if they want people to be able to switch easily between managed and native worlds then zero allocation is desirable since that's what fits with the managed code.

Records in Delphi

some questions about records in Delphi:
As records are almost like classes, why not use only classes instead of records?
In theory, memory is allocated for a record when it is declared by a variable; but, and how is memory released after?
I can understand the utility of pointers to records into a list object, but with Generics Containers (TList<T>), are there need to use pointer yet? if not, how to delete/release each record into a Generic Container? If I wanna delete a specific record into a Generic Container, how to do it?
There are lots of differences between records and classes; and no "Pointer to record" <> "Class". Each has its own pros and cons; one of the important things about software development is to understand these so you can more easily choose the most appropriate for a given situation.
This question is based on a false premise. Records are not almost like classes, in the same way that Integers are not almost like Doubles.
Classes must always be dynamically instantiated, whereas this is a possibility, but not a requirement for records.
Instances of classes (which we call objects) are always passed around by reference, meaning that multiple sections of code will share and act on the same instance. This is something important to remember, because you may unintentionally modify an object as a side-effect; although when done intentionally it's a powerful feature. Records on the other hand are passed by value; you need to explicitly indicate if you're passing them by reference.
Classes do not 'copy as easily as records'. When I say copy, I mean a separate instance duplicating a source. (This should be obvious in light of the value/reference comment above).
Records tend to work very nicely with typed files (because they're so easy to copy).
Records can overlay fields with other fields (case x of/unions)
These were comments on certain situational benefits of records; conversely, there are also situational benefits for classes that I'll not elaborate on.
Perhaps the easiest way to understand this is to be a little pedantic about it. Let's clarify; memory is not really allocated 'when its declared', it's allocated when the variable is in scope, and deallocated when it goes out of scope. So for a local variable, it's allocated just before the start of the routine, and deallocated just after the end. For a class field, it's allocated when the object is created, and deallocated when it's destroyed.
Again, there are pros and cons...
It can be slower and require more memory to copy entire records (as with generics) than to just copy the references.
Passing records around by reference (using pointers) is a powerful technique whereby you can easily have something else modify your copy of the record. Without this, you'd have to pass your record by value (i.e. copy it) receive the changed record as a result, copy it again to your own structures.
Are pointers to records like classes? No, not at all. Just two of the differences:
Classes support polymorphic inheritance.
Classes can implement interfaces.
For 1 and 2: records are value types, while classes are reference types. They're allocated on the stack, or directly in the memory space of any larger variable that contains them, instead of through a pointer, and automatically cleaned up by the compiler when they go out of scope.
As for your third question, a TList<TMyRecord> internally declares an array of TMyRecord for storage space. All the records in it will be cleaned up when the list is destroyed. If you want to delete a specific one, use the Delete method to delete by index, or the Remove method to find and delete. But be aware that since it's a value type, everything you do will be making copies of the record, not copying references to it.
One of the main benefits of records is, when you have a large "array of record". This is created in memory by allocating space for all records in one contiguous RAM space, which is extremely fast. If you had used "array of TClass" instead, each object in the array would have to be allocated by itself, which is slow.
There has been a lot of work to improve the speed of allocating memory, in order to improve the speed of strings and objects, but it will never be as fast as replacing 100,000 memory allocations with 1 memory allocation.
However, if you use array of record, don't copy the record around in local variables. That may easily kill the speed benefit.
1) To allow for inheritance and polymorphism, classes have some overhead. Records do not allow them, and in some situations may be somewhat faster and simpler to use. Unlike classes, that are always allocated in the heap and managed through references, records can be allocated on the stack also, accessed directly, and assigned each other without requiring to call an "Assign" method.
Also records are useful to access memory blocks with a given structure, because their memory layout is exactly how you define it. A class instance memory layout is controlled by the compiler and has additional data to make objects work (i.e. the pointer to the Virtual Method Table).
2) Unless you allocate records dynamically, using New() or GetMem(), record's memory is managed by the compiler as ordinals, floats or static arrays: global variables memory is allocated at startup and released when the program terminates, and local variables are allocated on the stack entering a function/procedure/method and released exiting. Allocating/releasing memory in the stack is faster because it doesn't require calls to the memory manager, it's just very few assembler instructions to change the stack registers. But be aware that allocating large structure on the stack may cause a stack overflow, because the maximum stack size is fixed and not very large (see linker options).
If records are fields of a class, they are allocated when the class is created and released when the class is freed.
3) One of the advantages of generics is to eliminate the need of low-level pointer management - but be aware of the inner workings.
There are a few other differences between a class and a record. Classes can use polymorphism, and expose interfaces. Records can not implement destructors (although since Delphi 2006 they can now implement constructors and methods).
Records are very useful in segmenting memory into a more logical structure since the first data item in the record is at the same address point of the pointer to the record itself. This is not the case for classes.

Resources