记录中的接口字段

时间:2011-11-28 09:14:41

标签: delphi delphi-xe

我可以依赖这样一个事实,即记录中的接口字段始终初始化为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但是当包含在记录中时也是如此吗?

1 个答案:

答案 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;