我可以依赖这样一个事实,即记录中的接口字段始终初始化为nil
吗?
TMyRec = record
FGuard : IInterface;
FObject : TObject;
procedure CheckCreated;
end;
这将允许我写:
procedure TMyCheck.CheckCreated;
begin
if (FGuard = nil) then
begin
FObject := TObject.Create;
FGuard := TGuard.Create (FObject);
end;
end;
(用于自动终身管理)
我知道接口字段初始化为nil
但是当包含在记录中时也是如此吗?
答案 0 :(得分:9)
是的,你可以依靠它。
所有参考计数变量:
nil
时,被初始化为record
,如果使用New
或动态数组 - 甚至在堆栈本地。当然,如果你使用普通GetMem
或使用指针,你必须自己初始化它(例如使用FillChar
)。
如果您感到好奇,则会对System.pas的以下过程进行隐藏调用:
procedure _InitializeRecord(p: Pointer; typeInfo: Pointer);
这会将所有引用计数变量内存填充为0,但不会设置record
的其他成员。实际上,在class
实例中,整个字段内存初始化为0,包括所有成员 - 对于record
,初始化仅用于引用计数类型。
请注意,在某些情况下,如果您使用object
类型而非record
- at least under Delphi 2009-2010,我发现未正确生成此初始化。因此,如果您的代码有一些object
类型声明,您最好切换到record
(和松散的继承),或明确调用FillChar
。
如果您感到好奇,这是我在asm中编写的优化版本 - 可在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;