接口字段记录 [英] Interface field in record
问题描述
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屋!