17

In my application I have the following record:

TTransaction = record
  Alias: string
  Description: string
  Creation: TDateTime
  Count: Integer
end;

and I'm using this record in this array:

Transactions = array of TTransaction;

I'm keeping the array loaded during runtime, but at a given time I need to clear all data and add some new.

Is it enough just to use:

SetLength(Transactions, 0); ?

Or do I need to finalize something ?

TLama
  • 71,521
  • 15
  • 192
  • 348
EProgrammerNotFound
  • 2,333
  • 4
  • 21
  • 54

3 Answers3

17

There are three ways to deallocate the memory associates with a dynamic array, a:

SetLength(a, 0);
Finalize(a);
a := nil;

It's up to you which one to use.

The documentation says the same, albeit in a slightly round about fashion:

To deallocate a dynamic array, assign nil to a variable that references the array or pass the variable to Finalize; either of these methods disposes of the array, provided there are no other references to it. Dynamic arrays are automatically released when their reference-count drops to zero. Dynamic arrays of length 0 have the value nil.

This will release all memory associated with the array, including any nested managed types, such as strings, dynamic arrys etc. that are owned by your record type.

If you need to resize the array for future use, and have the new data available, simply resize using SetLength, and initialise the remaining elements appropriately.

David Heffernan
  • 572,264
  • 40
  • 974
  • 1,389
  • 1
    Does `Finalize` actually change the value of `a`, though? I've always been under the impression it doesn't — that it frees the memory while leaving the original address of the dynamic array stored in `a`, in anticipation of a subsequent call to either `Free` or `Initialize`. – Rob Kennedy Feb 19 '13 at 13:29
  • 1
    @Rob For a dynamic array, these three options are all equivalent. For a managed type, Finalize has to set the reference to nil. It has no choice. Consider a local variable of type dynamic array. – David Heffernan Feb 19 '13 at 13:34
  • @DavidHeffernan In cases where the highest bounds of the array is 10, wich is better to use: 1- Set to Nil and recreate all. 2 - Associate the used indexes to the next values?? – EProgrammerNotFound Feb 19 '13 at 18:11
  • Probably doesn't matter. Whichever you prefer. – David Heffernan Feb 19 '13 at 18:32
11

Setting the array length to zero will destroy the array, which goes counter to your desire to "keep the array loaded." However, it will free the memory for all the records and their strings (for any strings whose reference count is 1 at the time).

If you just want to free the memory for the strings, but keep the record memory allocated (because you plan to allocate another set of records immediately afterward, and you don't want the waste of releasing and re-allocating the same memory), then you can clear just the string members, but there's no single library call to do it for you. Instead, you'll need to write a loop and clear each record's fields yourself.

for i := 0 to High(transactions) do begin
  transactions[i].alias := '';
  transactions[i].description := '';
end;

If there are lots of fields in the record that need clearing, then it might be more convenient to assign a default TTransaction value to each element of the array. You can use the Default function, or in older versions of Delphi you can declare a TTransaction that has all its fields clear already:

const
  ClearTransaction: TTransaction = (alias: ''; description: ''; creation: 0; count: 0);

for i := 0 to High(transactions) do
  transactions[i] := ClearTransaction;
  // or
  transactions[i] := Default(TTransaction);
Rob Kennedy
  • 156,531
  • 20
  • 258
  • 446
  • I think this is the best to do... since is the user who decides when he wants to reaload all data, i knew that record types is an struct and in my case of primitive types, this is why i asked. thanks for the answer – EProgrammerNotFound Feb 19 '13 at 13:39
  • 1
    Am existing question that touches on the topic covered in the second part of the answer here is: http://stackoverflow.com/questions/11065821/how-to-properly-free-records-that-contain-various-types-in-delphi-at-once – David Heffernan Feb 19 '13 at 14:37
  • @DavidHeffernan i read that topic and that's why i asked about finalization. I got confused with so many answers, and that guy speaking about finalize + fillchar... – EProgrammerNotFound Feb 19 '13 at 14:47
  • There was a ridiculous amount of noise in the other answers. Pointless ramblings with silly optimisation. The accepted answer there is what you need to read, even if I say so myself. – David Heffernan Feb 19 '13 at 16:22
  • You don't need explicit field emptying as you have `Finalize` to do that automatically. – Fr0sT Nov 05 '14 at 12:39
0

SetLength (transactions,0) is not a good idea. I think the best way is to reinitialize all the record's members. This way, you keep the variable loaded.

You can use SetLength (transactions,0) if you don't need the variable anymore, to use as little memory as possible. Of course, if you need it again somewhere in the program, you can re-adjust its length, suppose you know it.

You do not need to finalize anything, beause it's a record, not a class. Records do not have constructors or destructors, like classes.

Bogdan Doicin
  • 1,972
  • 5
  • 22
  • 28
  • 1
    *"you can re-adjust its length"* - but remember that then you'll probably get all the old items copied with all their static contents and the dynamic contents (strings, dyn arrays) probably will have their ref count increased/decreased. That's why I use full array deallocation before length changing in some cases. *"You do not need to finalize anything, beause it's a record, not a class"* - that's true only if all record fields are simple types (not strings/dyn arrays/interfaces). – Fr0sT Nov 05 '14 at 12:47