Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi中接口的内存管理_Delphi_Memory Management_Interface_Access Violation - Fatal编程技术网

Delphi中接口的内存管理

Delphi中接口的内存管理,delphi,memory-management,interface,access-violation,Delphi,Memory Management,Interface,Access Violation,我来自C#,正在努力学习delphi和内存管理 这场斗争的当前体现是,当我处理完这些物品后,我不知道如何正确处置它们。从阅读和我的实验来看,如果我有一个对象被转换为接口,那么我唯一的选择就是将引用设置为nil。 如果我去打电话 FreeAndNil() 我最终遭到访问违规,例如: var foo: IFoo; begin foo := TFoo.Create(); FreeandNil(foo); end; 当然,我所需要做的就是改变foo:IFoo;tofoo:TFoo;它是快乐

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

这场斗争的当前体现是,当我处理完这些物品后,我不知道如何正确处置它们。从阅读和我的实验来看,如果我有一个对象被转换为接口,那么我唯一的选择就是将引用设置为nil。 如果我去打电话 FreeAndNil() 我最终遭到访问违规,例如:

var
  foo: IFoo;
begin
  foo := TFoo.Create();
  FreeandNil(foo);
end;
当然,我所需要做的就是改变foo:IFoo;tofoo:TFoo;它是快乐的。或者简单地将指针设置为nil,而不是调用freeandNil

foo := nil;
所以,在一个层面上,我一点也不明白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;
测试代码:

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)


但我要向德尔福社区表示衷心的感谢。你们是最好的学习对象

如果我们通过接口变量访问某个对象,并不总是意味着当引用计数器降为零时该对象就被破坏了。例如,
TComponent
methods\u AddRef和_Release实现是“虚拟的”:没有实现引用计数,TComponent永远不会被销毁,因为接口变量超出范围

要按照我们对“真实”接口的期望进行操作,您的所有对象都应该是
TInterfacedObject
的后代,或者您需要自己实现
\u AddRef
/
\u Release

是的,有两种不同的内存管理方法,它们通常共存于一个程序中,但只有当以两种方式处理同一对象时,才会出现混淆(和AV)。如果我们销毁了对象,只有在接口变量超出范围时,它们才会调用销毁对象的
\u Release
方法,从而导致访问冲突。这是一项有风险的业务,尽管经过一定的关注是可行的

经典的Delphi组件没有引用计数,而是使用所有权的概念。每个组件都有一个所有者,其责任是在其自身被破坏时释放所有内存。所以每个组件都有一个所有者,但它也可能有很多指向其他组件的指针,比如工具栏上有
ImageList
变量时。如果这样的组件被重新计数,它们将永远不会因为循环引用而被破坏,因此为了打破这个循环,您还需要不“计数”的“弱”引用。它们也在这里,但这是Delphi最近的特性

如果您的对象中存在某种层次结构,因此您知道“较大”的对象需要所有“较小”的对象才能运行,那么请使用这种好的旧方法,它非常简单,并且在Delphi中有很好的实现,即:您可以生成一个无泄漏的代码,无论在何处出现异常。有很多小事情,比如使用
.Free
而不是
.Destroy
,因为如果构造函数中发生异常,就会自动调用析构函数,等等。事实上,这是一个非常聪明的解决方案


只有当您不知道某个对象需要多长时间,并且没有合适的“所有者”时,我才会使用refcounted接口。我把扫描的图像保存到一个线程的文件中,然后转换成更小的图像显示在另一个线程的屏幕上。当所有这些都完成后,RAM中不再需要图像,图像可以被销毁,但我不知道哪一个先发生。在这种情况下,使用refcounting是最好的选择。

访问冲突的原因是
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
完全不引用对象,而是引用其他对象(例如接口、记录、整数,因为它们不实现
Free
,如果它们实现了,它的位置将与
TObject.Free
)不同,那么它也可能被检测到

在另一个层次上,我想编写代码,这样它就不需要知道它是一个接口还是一个对象。我希望能够以同样的方式编写所有内存管理

这就像说你想用和淋浴完全一样的方式使用你的汽车。
好吧,也许差别并没有那么大。但关键是接口和对象(以及记录)使用不同的内存管理范式。你不能用同样的方法管理他们的记忆

  • 需要显式销毁对象。您可以使用所有权模型,但销毁仍然是一个明确的外部操作
  • 接口被引用计数。编译器注入代码以跟踪引用(查看)底层实例的字段和变量的数量。通常,对象会被销毁
    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;