type
TSomeRecord = Record
field1: integer;
field2: string;
field3: boolean;
End;
var
SomeRecord: TSomeRecord;
SomeRecAr: array of TSomeRecord;
这是我所拥有的最基本的例子,因为我想重用SomeRecord
(某些字段保持为空,而不释放所有字段,当我重用SomeRecord
时,某些字段会被转移,这显然是不受欢迎的)我正在寻找一种方法来立即释放所有的领域。我开始使用string[255]
并使用ZeroMemory()
,这很好,直到它开始泄漏内存,这是因为我切换到string
。我仍然缺乏了解原因的知识,但它似乎与它的动态有关。我也在使用动态数组,因此我假设在任何动态上尝试ZeroMemory()
都会导致泄漏。有一天浪费了这一点。我想我在Finalize()
之前使用SomeRecord
或SomeRecAr
之前使用ZeroMemory()
解决了这个问题,但我不确定这是正确的方法还是仅仅是我的愚蠢。< / p>
所以问题是:如何一次性释放所有东西?是否存在一些我不知道的单一程序?
另一方面,或者我会建议如何以不同的方式实现这些记录,所以我不需要做出复杂的尝试来释放东西。我已经研究了使用New()
创建记录然后摆脱它Dispose()
,但我不知道在调用Dispose()
之后的变量未定义时它意味着什么,而不是零。另外,我不知道某个类型的变量(SomeRecord: TSomeRecord
)与指向类型的变量(SomeRecord: ^TSomeRecord
)之间的区别。我正在调查上述问题,除非有人能够快速解释,否则可能需要一些时间。
答案 0 :(得分:27)
假设您有一个支持在记录上实现方法的Delphi版本,您可以清除这样的记录:
type
TSomeRecord = record
field1: integer;
field2: string;
field3: boolean;
procedure Clear;
end;
procedure TSomeRecord.Clear;
begin
Self := Default(TSomeRecord);
end;
如果您的编译器不支持Default
,那么您可以完全按照以下方式执行相同操作:
procedure TSomeRecord.Clear;
const
Default: TSomeRecord=();
begin
Self := Default;
end;
您可能更愿意避免在方法中改变值类型。在这种情况下,创建一个返回空记录值的函数,并将其与赋值运算符一起使用:
type
TSomeRecord = record
// fields go here
class function Empty: TSomeRecord; static;
end;
class function TSomeRecord.Empty: TSomeRecord;
begin
Result := Default(TSomeRecord);
end;
....
Value := TSomeRecord.Empty;
顺便说一句,我找不到Default(TypeIdentifier)
的任何文档参考。有谁知道它在哪里可以找到?
至于你问题的第二部分,我认为没有理由不继续使用记录,并使用动态数组分配它们。试图自己管理生命周期更容易出错。
答案 1 :(得分:7)
不要让思维过于复杂!
分配“默认”record
只会损失CPU功耗和内存。
在record
内声明TClass
时,它会填充零,因此已初始化。当它在堆栈上分配时,只初始化引用计数变量:其他类型的变量(如整数或双精度或布尔值或枚举)处于随机状态(可能非零)。当它将在堆上分配时,getmem
将不会初始化任何内容,allocmem
将填充所有内容为零,new
将仅初始化引用计数成员(如在堆栈初始化时) ):在所有情况下,您应使用dispose
或finalize+freemem
来释放堆分配的record
。
关于您的确切问题,您自己的假设是正确的:要在使用后重置记录内容,请不要使用“fillchar
”(或“zeromemory
”)而不使用之前的“{{1} }”。这是正确和最快的方式:
finalize
再次,它将比分配默认记录更快。并且在所有情况下,如果您使用Finalize(aRecord);
FillChar(aRecord,sizeof(aRecord),0);
,甚至多次,它将不会泄漏任何内存 - 100%退款保修!
编辑:查看Finalize
生成的代码后,代码已经过优化:实际上它是aRecord := default(TRecordType)
+ Finalize
个模仿stosd
。因此,即使语法是副本/分配(FillChar
),也不会将其实现为副本/赋值。我的错误在这里。
但我仍然不喜欢必须使用:=
这一事实,其中Embarcadero应该更好地使用像:=
这样的record
方法作为语法,就像{{3} }}。实际上,这个aRecord.Clear
语法与C#使用的语法完全相同。听起来如果Embacardero在任何地方模仿C#语法,却没有发现DelphiWebScript's dynamic arrays。如果德尔福只是一个追随者,而不是实施者认为“它的方式”是什么意思呢?人们总是更喜欢原始的C#到它的祖先(德尔福有同样的父亲)。
答案 2 :(得分:5)
我想到的最简单的解决方案是:
const
EmptySomeRecord: TSomeRecord = ();
begin
SomeRecord := EmptySomeRecord;
但要解决问题的所有其余部分,请采用以下定义:
type
PSomeRecord = ^TSomeRecord;
TSomeRecord = record
Field1: Integer;
Field2: String;
Field3: Boolean;
end;
TSomeRecords = array of TSomeRecord;
PSomeRecordList = ^TSomeRecordList;
TSomeRecordList = array[0..MaxListSize] of TSomeRecord;
const
EmptySomeRecord: TSomeRecord = ();
Count = 10;
var
SomeRecord: TSomeRecord;
SomeRecords: TSomeRecords;
I: Integer;
P: PSomeRecord;
List: PSomeRecordList;
procedure ClearSomeRecord(var ASomeRecord: TSomeRecord);
begin
ASomeRecord.Field1 := 0;
ASomeRecord.Field2 := '';
ASomeRecord.Field3 := False;
end;
function NewSomeRecord: PSomeRecord;
begin
New(Result);
Result^.Field1 := 0;
Result^.Field2 := '';
Result^.Field3 := False;
end;
接下来是一些关于如何操作它们的例子:
begin
// Clearing a typed variable (1):
SomeRecord := EmptySomeRecord;
// Clearing a typed variable (2):
ClearSomeRecord(SomeRecord);
// Initializing and clearing a typed array variabele:
SetLength(SomeRecords, Count);
// Creating a pointer variable:
New(P);
// Clearing a pointer variable:
P^.Field1 := 0;
P^.Field2 := '';
P^.Field3 := False;
// Creating and clearing a pointer variable:
P := NewSomeRecord;
// Releasing a pointer variable:
Dispose(P);
// Creating a pointer array variable:
ReallocMem(List, Count * SizeOf(TSomeRecord));
// Clearing a pointer array variable:
for I := 0 to Count - 1 do
begin
Pointer(List^[I].Field2) := nil;
List^[I].Field1 := 0;
List^[I].Field2 := '';
List^[I].Field3 := False;
end;
// Releasing a pointer array variable:
Finalize(List^[0], Count);
根据需要选择和/或组合。
答案 3 :(得分:1)
使用SomeRecord: TSomeRecord
,SomeRecord
将是TSomeRecord
类型的实例/变量。对于SomeRecord: ^TSomeRecord
,SomeRecord
将是指向TSomeRecord
类型的实例或变量的指针。在最后一种情况下,SomeRecord
将是一个类型指针。如果您的应用程序在例程之间传输大量数据或与外部API交互,则建议使用类型指针。
new()
和dispose()
仅用于键入指针。对于类型指针,编译器不具有应用程序使用此类变量的内存的控制/知识。由你来释放类型指针使用的内存。
另一方面,当你使用普通变量时,根据使用和声明,编译器会在它认为不再需要时释放它们使用的内存。例如:
function SomeStaff();
var
NativeVariable: TSomeRecord;
TypedPointer: ^TSomeRecord;
begin
NaviveVariable.Field1 := 'Hello World';
// With typed pointers, we need to manually
// create the variable before we can use it.
new(TypedPointer);
TypedPointer^.Field1 := 'Hello Word';
// Do your stuff here ...
// ... at end, we need to manually "free"
// the typed pointer variable. Field1 within
// TSomerecord is also released
Dispose(TypedPointer);
// You don't need to do the above for NativeVariable
// as the compiler will free it after this function
// ends. This apply also for native arrays of TSomeRecord.
end;
在上面的示例中,变量NativeVariable
仅在SomeStaff
函数中使用,因此编译器会在函数结束时自动释放它。这适用于几乎大多数本机变量,包括数组和记录“字段”。对象的处理方式不同,但这是另一篇文章。