我正在努力学习来自 C# 的 delphi 和内存管理。
这种斗争的当前体现是,当我用完这些元素时,我不知道如何处理它们。从阅读和我的实验来看,如果我有一个被转换为接口(interface)的对象,那么我唯一的选择是将引用设置为 nil。 如果我去打电话 FreeAndNil() 我最终遇到了访问冲突,例如:
var
foo: IFoo;
begin
foo := TFoo.Create();
FreeandNil(foo);
end;
当然,我需要做的就是改变 foo:IFoo;到 foo:TFoo;它很高兴。或者简单地将指针设置为 nil,而不是调用 freeandNil。
foo := nil;
所以,在某种程度上,我根本不明白反病毒在哪里。
在不同的层面上,我想编写代码,使其不需要知道它是接口(interface)还是对象。我希望能够以相同的方式编写所有内存管理,但我似乎无法编写可以处理类或接口(interface)的方法。好吧,那不是真的,我确实有一些东西,但它太丑了,我犹豫是否要发布它。
但我想我也应该问,其他人在做什么?在心里记住什么是接口(interface)并且只是将这些指针置零?否则调用 FreeAndNil?
我第一次想将事情作为一个具体的类来实现,但是当我发现代码可以通过两种不同的方式来完成某些工作时,我会回来将其更改为接口(interface)。我不想浏览代码并改变它处理该引用的方式,这是我当时最不想做的事情。
但为了讨论,我最好的(几乎唯一)想法是这个类:
interface
type
TMemory = class(TObject)
class procedure Free(item: TObject); overload; static;
class procedure Free<T: IInterface>(item: T); overload; static;
end;
implementation
uses
System.SysUtils;
{ TMemory }
class procedure TMemory.Free(item: TObject);
begin
FreeandNil(item);
end;
class procedure TMemory.Free<T>(item: T);
begin
//don't do anything, it is up the caller to always nil after calling.
end;
然后我就可以持续调用:
TMemory.Free(Thing);
Thing := nil;
测试代码:
procedure TDoSomething.MyWorker;
var
foo: IFoo;
fooAsClass: TFoo;
JustAnObject: TObject;
begin
foo := TFoo.Create();
fooAsClass := TFoo.Create();
JustAnObject := TObject.Create();
TMemory.Free(foo);
foo := nil;
TMemory.Free(fooAsClass);
fooAsClass := nil;
TMemory.Free(JustAnObject);
JustAnObject := nil;
end;
运行时没有泄漏或访问违规。 (使用 MadExcept)
非常感谢 Delphi 社区。你们是最好的学习者!
最佳答案
访问冲突的原因是 FreeAndNil
采用非类型化参数,但期望它是一个东西。所以该方法对对象进行操作。
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj); //Obj must be a TObject otherwise all bets are off
Pointer(Obj) := nil; //Will throw an AV if memory violation is detected
Temp.Free; //Will throw an AV if memory violation is detected
end;
如果您销毁先前已销毁或从未创建的对象,则可能会检测到上述内存违规(注意不保证)。如果 Obj
根本不引用任何对象,而是引用其他对象(例如接口(interface)、记录、Integer,因为它们没有实现 Free
),也可能会被检测到如果他们这样做了,它的定位方式将不会与 TObject.Free
相同)。
On a differently level, I want to write the code such that it does not need to know if it is an interface or an object. I want to be able to write all of my memory management the same exact way.
这就像说您想以与使用淋浴完全相同的方式使用汽车。
好吧,也许差异并没有那么极端。但关键是接口(interface)和对象(以及就此而言的记录)使用不同内存管理范式。您无法以同样的方式管理他们的内存。
- 对象需要显式销毁。您可以使用所有权模型,但销毁仍然是一个明确的外部操作。
- 接口(interface)采用引用计数。编译器注入(inject)代码来跟踪引用(查看)底层实例的字段和变量的数量。通常,当释放最后一个引用时,对象会自行销毁。 (有一些方法超出了这个答案的范围来改变这一点。)
关于delphi - Delphi中接口(interface)的内存管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43792357/