Delphi中接口的内存管理

Jas*_*mis 2 delphi memory-management interface access-violation

我正在努力学习来自C#的delphi和内存管理.

目前这种斗争的化身是,当我完成它们时,我不知道处理物体的正确方法.从阅读和我的实验看来,如果我有一个被转换为接口的对象,那么我的唯一选择是将引用设置为nil.如果我去打电话FreeAndNil()我最终得到一个访问冲突,EX:

var
  foo: IFoo;
begin
  foo := TFoo.Create();
  FreeandNil(foo);
end;
Run Code Online (Sandbox Code Playgroud)

当然,我需要做的就是改变那个foo:IFoo; foo:TFoo; 它很开心 或者只是将指针设置为nil,而不是调用freeandNil.

foo := nil;
Run Code Online (Sandbox Code Playgroud)

所以,在一个层面上,我至少不了解AV的位置.

在不同的层面上,我想编写代码,使得它不需要知道它是接口还是对象.我希望能够以同样的方式编写所有内存管理,但我似乎无法编写一个可以处理类或接口的方法.嗯,这不是真的,我确实有一些东西,但它是如此丑陋,我犹豫发布它.

但我想我也应该问,其他人在做什么?精神上跟踪什么是界面,只是那些指针?否则调用FreeAndNil?

我想要第一次实现作为具体类的东西,但后来回来并将其更改为接口,当我找到代码可以通过两种不同方式进行操作时.而且我不打算通过代码来改变处理该引用的方式,这是我当时最后的想法.

但是为了讨论,我最好的(几乎是唯一的)想法就是这个课程:

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;
Run Code Online (Sandbox Code Playgroud)

然后我可以一直打电话:

TMemory.Free(Thing);
Thing := nil;
Run Code Online (Sandbox Code Playgroud)

测试代码:

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;
Run Code Online (Sandbox Code Playgroud)

运行时没有泄漏或访问冲突.(使用MadExcept)

但非常感谢德尔福社区的SO.你们这是学习中最好的东西!

Dis*_*ned 7

访问冲突的原因是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;
Run Code Online (Sandbox Code Playgroud)

内存违反上述威力(NB不能保证),如果您销毁或者已被毁坏之前或从未创建的对象进行检测.它也可能被检测到,如果Obj根本没有引用一个对象而是其他东西(例如接口,记录,整数,因为它们没有实现Free,如果它们没有实现,它将不会以相同的方式定位TObject.Free) .

在不同的层面上,我想编写代码,使得它不需要知道它是接口还是对象.我希望能够以同样的方式编写所有内存管理.

这就像是说你想要使用你的汽车,就像你使用淋浴一样.
好吧,也许差异不是那么极端.但关键是接口和对象(以及对于那个事件记录)使用不同的内存管理范例.你无法以同样的方式管理他们的记忆.

  • 需要明确销毁对象.您可以使用所有权模型,但销毁仍然是明确的外部操作.
  • 接口是参考计数.编译器注入代码以跟踪引用(查看)底层实例的字段和变量的数量.通常,对象在释放最后一个引用时会自行销毁.(有些方法超出了这个答案的范围来改变这个.)