Поле интерфейса в записи - PullRequest
3 голосов
/ 28 ноября 2011

Могу ли я полагаться на тот факт, что поле интерфейса в записи всегда инициализируется как 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 Ответ

9 голосов
/ 28 ноября 2011

Да, на это можно положиться.

Все переменные с подсчетом ссылок:

  • Струны;
  • Динамические массивы;
  • Варианты;
  • Интерфейсы;
  • Вложенные записи, содержащие такие переменные.

инициализируются на nil при выделении record, если вы используете New или динамический массив - даже локально в стеке. Конечно, если вы используете простой GetMem или работаете с указателями, вам придется инициализировать его самостоятельно (например, с помощью FillChar).

Если вам интересно, есть скрытый вызов следующей процедуры System.pas:

procedure _InitializeRecord(p: Pointer; typeInfo: Pointer);

Это заполнит всю память переменных с подсчетом ссылок на 0, но не установит другие члены record. Фактически, в экземпляре class вся память поля инициализируется с 0, включая все элементы - для record инициализация только для типов с подсчетом ссылок.

Обратите внимание, что в некоторых случаях я обнаружил, что эта инициализация не была сгенерирована должным образом, если вы используете тип object вместо record - по крайней мере в Delphi 2009-2010 . Поэтому, если в вашем коде есть какое-то объявление типа object, вам лучше перейти на record (и потерять наследование) или явно вызвать FillChar.

Если вам интересно, вот оптимизированная версия, которую я написал в asm - доступна в нашем улучшенном 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;
...