接口字段记录 [英] Interface field in record

查看:141
本文介绍了接口字段记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以依靠这个事实,记录中的一个界面字段始终被初始化为 nil

  TMyRec = record 
FGuard:IInterface;
FObject:TObject;
程序CheckCreated;
结束

这将允许我写:

 程序TMyCheck.CheckCreated; 
begin
if(FGuard = nil)then
begin
FObject:= TObject.Create;
FGuard:= TGuard.Create(FObject);
结束
结束

(用于自动终身管理)



我知道接口字段被初始化为 nil ,但是当包含在记录中时也是如此?

解决



所有引用计数的变量:




  • 字符串;

  • 动态数组;

  • 变体;


  • 包含这些变量的嵌套记录。



初始化为新建或动态数组,则分配记录时,甚至在本地堆叠。当然,如果你使用一个简单的 GetMem 或使用指针,你必须自己初始化它(例如使用一个 FillChar )。



如果您好奇,则会对System.pas的以下过程隐藏调用:

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

这将把所有引用计数的变量内存填充为0,但不会设置其他成员的记录。实际上,在一个实例中,整个字段内存被初始化为0,包括所有成员 - 记录 ,初始化仅用于引用计数的类型。



请注意,在某些情况下,我发现如果使用对象类型而不是记录 - 至少在Delphi 2009-2010中。所以如果你的代码有一些对象类型声明,你最好切换到记录(和松散继承)调用 FillChar



如果你好奇,这里是我在asm中写的一个优化版本 - 可在我们的增强型RTL

  procedure _InitializeRecord(p:Pointer; typeInfo:Pointer); 
//这个过程在大多数对象创建中被调用 - >优化岩石在这里!
asm
{ - > EAX指针,以记录初始化}
{EDX指针类型信息}
MOVZX ECX,[EDX + 1] {类型名称长度}
PUSH EBX
PUSH ESI
PUSH EDI
MOV EBX,EAX // PIC安全。见上面的评论
LEA ESI,[EDX + ECX + 2 + 8] {可破坏领域的地址}
MOV EDI,[EDX + ECX + 2 + 4] {可破坏领域的数量}
@@ loop:
mov edx,[esi] //键入info
mov eax,[esi + 4]
mov edx,[edx]
add esi,8
添加eax,ebx //要初始化的数据
movzx ecx,[edx] //数据类型
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:
@@接口:
@@ DynArray:// EAX
中的零4个字节b dec edi
mov dword ptr [eax],0
jg @@ loop
POP EDI
POP ESI
POP EBX
RET
@@变式://零EAX $ 16 $ b中的16个字节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错误
@@数组:
@@记录://在实践中很少调用
mov ecx,1
调用_InitializeArray
dec edi
jg @@ loop
@@ exit:
POP EDI
POP ESI
POP EBX
结束;


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:

  • Strings;
  • Dynamic arrays;
  • Variants;
  • Interfaces;
  • Nested records containing those kind of 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;

这篇关于接口字段记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆