Can I rely on the fact, that an interface field in a record is always initialized to nil?
TMyRec = record
FGuard : IInterface;
FObject : TObject;
procedure CheckCreated;
end;
This would allow me to write:
procedure TMyCheck.CheckCreated;
begin
if (FGuard = nil) then
begin
FObject := TObject.Create;
FGuard := TGuard.Create (FObject);
end;
end;
(for automatic lifetime management)
I know that interface fields are initialized to nil but is that also true when contained in a record?
Yes you can rely on that.
All reference-counted variables:
are initialized to nil when a record is allocated, if you use New or a dynamic array - even locally on the stack. Of course, if you use a plain GetMem or work with pointers, you'll have to initialize it by yourself (e.g. using a FillChar).
If you are curious, there is an hidden call to the following procedure of System.pas:
procedure _InitializeRecord(p: Pointer; typeInfo: Pointer);
This will fill all the reference-counted variables memory to 0, but won't set the other members of the record. In fact, in a class instance, the whole field memory is initialized with 0, including all members - for a record, the initialization is only for reference-counted types.
Note that in some cases, I've found out that this initialization was not properly generated if you use the object type instead of record - at least under Delphi 2009-2010. So if your code has some object type declaration, you may better switch to record (and loose inheritance), or explicitly call FillChar.
If you are curious, here is an optimized version I wrote in asm - available in our enhanced RTL.
procedure _InitializeRecord(p: Pointer; typeInfo: Pointer);
// this procedure is called at most object creation -> optimization rocks here!
asm
{ -> EAX pointer to record to be initialized }
{ EDX pointer to type info }
MOVZX ECX,[EDX+1] { type name length }
PUSH EBX
PUSH ESI
PUSH EDI
MOV EBX,EAX // PIC safe. See comment above
LEA ESI,[EDX+ECX+2+8] { address of destructable fields }
MOV EDI,[EDX+ECX+2+4] { number of destructable fields }
@@loop:
mov edx,[esi] // type info
mov eax,[esi+4]
mov edx,[edx]
add esi,8
add eax,ebx // data to be initialized
movzx ecx,[edx] // data type
cmp ecx,tkLString
je @@LString
jb @@err
cmp ecx,tkDynArray
je @@DynArray
ja @@err
jmp dword ptr [ecx*4+@@Tab-tkWString*4]
nop; nop; nop // align @@Tab
@@Tab: dd @@WString,@@Variant,@@Array,@@Record
dd @@Interface,@@err
@@LString:
@@WString:
@@Interface:
@@DynArray: // zero 4 bytes in EAX
dec edi
mov dword ptr [eax],0
jg @@loop
POP EDI
POP ESI
POP EBX
RET
@@Variant: // zero 16 bytes in EAX
xor ecx,ecx
dec edi
mov [eax],ecx
mov [eax+4],ecx
mov [eax+8],ecx
mov [eax+12],ecx
jg @@loop
jmp @@exit
@@err:
MOV AL,reInvalidPtr
POP EDI
POP ESI
POP EBX
JMP Error
@@Array:
@@Record: // rarely called in practice
mov ecx,1
call _InitializeArray
dec edi
jg @@loop
@@exit:
POP EDI
POP ESI
POP EBX
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With