2011-11-28 42 views
3

我可以依靠記錄中的接口字段總是初始化爲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

請問下降者請解釋她或他的推理嗎? – jpfollenius

回答

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*[email protected]@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; 
+0

+1非常感謝!任何想法爲什麼決定不使用'FillChar'來簡單地初始化整個記錄,這會爲我們提供其他數據類型的初始值? – jpfollenius

+0

@Smasher因爲這可能會迫使每個人支付運行時成本,當可能不是每個人都想零初始化時。另外,如果您動態分配記錄,則需要使用New/Dispose來獲取此行爲。 GetMem/Free會失敗,因爲它們缺少類型信息。 –

+0

@David:您確定運行時成本(一次調用'ZeroMemory')是否會高於有選擇地初始化託管類型的代碼? – jpfollenius