【问题标题】:Reference counted object within a record not destroyed when record goes out of scope当记录超出范围时,记录中的引用计数对象不会被破坏
【发布时间】:2016-11-21 17:27:58
【问题描述】:

我有一条记录,其中包含我认为是指向引用计数对象的指针。我希望如果我在记录中创建引用计数对象,当记录超出范围时,对象的引用计数将降至零,并且对象将被销毁。但情况似乎并非如此。这是示例最小代码。我的表单恰好有一些面板和备忘录,但只有 TButton(特别是 Button1Click)很重要。

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TUserData = class( TInterfacedObject )
  public
    AData : integer;

    constructor Create;
    destructor Destroy; override;
  end;

  TTestRec = Record
    AField : integer;
    UserData : TUserData;
  End;

  TForm4 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Panel3: TPanel;
    Memo1: TMemo;
    Button1: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form4: TForm4;

implementation

{$R *.dfm}

procedure TForm4.Button1Click(Sender: TObject);
var
  iRec : TTestRec;
begin
   iRec.UserData := TUserData.Create;
   // stop too much optimisation
   Button1.Caption := IntToStr( iRec.UserData.AData );
end; // I would expect TTestRec and hence TTestRec.UserData to go out of scope here

procedure TForm4.FormShow(Sender: TObject);
begin
  // show leaks on exit
  ReportMemoryLeaksOnShutdown := TRUE;
end;

{ TUserData }

constructor TUserData.Create;
begin
  inherited Create;
  AData := 4;
end;

destructor TUserData.Destroy;
begin

  inherited;
end;

end.

我承认我并不真正了解引用计数的详细工作原理,尽管我确实了解其原理。我错过了什么?我是否期望过高,如果是这样,有什么方法可以避免内存泄漏,不是在这种特定情况下(显然我可以在退出时销毁 UserData),但总的来说,因为记录不支持析构函数。

【问题讨论】:

标签: delphi


【解决方案1】:

自动引用计数是通过接口变量执行的。你没有。您需要一个作为接口的变量,而不是TUserData 类型的变量。

你可以在这里使用IInterface,但这有点没用。因此,您应该定义一个接口,该接口公开您需要对象支持的公共功能,然后让您的类实现该接口。

这个程序演示了我的意思:

type
  IUserData = interface
    ['{BA2B50F5-9151-4F84-94C8-6043464EC059}']
    function GetData: Integer;
    procedure SetData(Value: Integer);
    property Data: Integer read GetData write SetData;
  end;

  TUserData = class(TInterfacedObject, IUserData)
  private
    FData: Integer;
    function GetData: Integer;
    procedure SetData(Value: Integer);
  end;

function TUserData.GetData: Integer;
begin
  Result := FData;
end;

procedure TUserData.SetData(Value: Integer);
begin
  FData := Value;
end;

type
  TTestRec = record
    UserData: IUserData;
  end;

procedure Main;
var
  iRec: TTestRec;
begin
  iRec.UserData := TUserData.Create;
end;

begin
  Main;
  ReportMemoryLeaksOnShutdown := True;
end.

此程序不会泄漏。将记录类型中的变量声明更改为UserData: TUserData,泄漏返回。

【讨论】:

  • 非常感谢您的解释。雾(慢慢地)开始消散。在阅读了 TInterfacedObject 文档后,它说它为引用计数引入了 support,a 曾假设(错误地)我正在创建一个引用计数对象。
  • 您正在创建一个引用计数对象。但是你需要一些东西来计算引用。这通过接口变量发生。
  • 实际上,对我来说,灯泡时刻是您评论声明必须是接口的最后一段,并将其定义为类会导致泄漏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-25
  • 1970-01-01
  • 2014-09-07
  • 2014-11-27
  • 1970-01-01
  • 1970-01-01
  • 2021-08-25
相关资源
最近更新 更多