当记录超出范围

时间:2016-11-21 17:27:59

标签: delphi

我有一条记录,其中包含我认为是指向引用计数对象的指针。我希望如果我在记录中创建引用计数对象,当记录超出范围时,对象的引用计数将降为零,并且对象将被销毁。但这似乎并非如此。这是最小代码示例。我的表格碰巧有一些面板和备忘录,但只有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)但一般来说,因为记录不支持析构函数。

1 个答案:

答案 0 :(得分:6)

自动引用计数通过接口变量执行。你什么都没有。而不是类型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,并返回泄漏。