2016-11-21 30 views
2

我有一條記錄,其中包含我認爲是指向引用計數對象的指針。我期望如果我在記錄中創建引用計數對象,當記錄超出範圍時,對象的引用計數將降至零,並且該對象將被銷燬。但這似乎並不是這種情況。這是示例最小代碼。我的表單恰好有一些面板和備忘錄,但只有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),但總的來說,因爲記錄不支持析構函數。

+0

看看http://stackoverflow.com/questions/3920470/why-arent-addref-and-release-called-on-my-delphi-object/,它應該有助於你理解發生了什麼更好。 –

回答

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並返回泄漏。

+0

非常感謝你的解釋。霧(緩慢)開始清除。在閱讀了TInterfacedObject文檔後,它說它引入了*支持*作爲引用計數,我曾假設(錯誤地)是我正在創建一個引用計數對象。 – Dsm

+1

您正在創建引用計數對象。但是你需要一些東西來計算參考。這是通過接口變量發生的。 –

+0

是的,現在我明白了。謝謝。 – Dsm