6

インターフェイスの自動参照カウントを使用することの短所に関する StackOverflow の多くの投稿を読んだ後、各インターフェイスのインスタンス化を手動で参照カウントしようとし始めました。

午後いっぱい試した後、あきらめました!

FreeAndNil(p) を呼び出すとアクセス違反が発生するのはなぜですか?

以下は、私の単純なユニットの完全なリストです。

unit fMainForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm4 = class(TForm)
    btn1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
  end;

type
  IPersona = interface(IInterface)
  ['{44483AA7-2A22-41E6-BA98-F3380184ACD7}']
    function GetNome: string;
    procedure SetNome(const Value: string);
    property Nome: string read GetNome write SetNome;
  end;

type
  TPersona = class(TObject, IPersona)
  strict private
    FNome: string;
    function GetNome: string;
    procedure SetNome(const Value: string);
  protected
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  public
    constructor Create(const ANome: string);
    destructor Destroy; override;
  end;

var
  Form4: TForm4;

implementation

{$R *.dfm}

procedure TForm4.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := True;
end;

procedure TForm4.btn1Click(Sender: TObject);
var
  p: IPersona;
begin
  p := TPersona.Create('Fabio');
  try
    ShowMessage(p.Nome);
  finally
    FreeAndNil(p);
  end;
end;

constructor TPersona.Create(const ANome: string);
begin
  inherited Create;
  FNome := ANome;
end;

destructor TPersona.Destroy;
begin
  inherited Destroy;
end;

function TPersona._AddRef: Integer;
begin
  Result := -1
end;

function TPersona._Release: Integer;
begin
  Result := -1
end;

function TPersona.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := S_OK
  else
    Result := E_NOINTERFACE;
end;

function TPersona.GetNome: string;
begin
  Result := FNome;
end;

procedure TPersona.SetNome(const Value: string);
begin
  FNome := Value;
end;

end.
4

2 に答える 2

11

アクセス違反はFreeAndNil、オブジェクト参照であると予想される型指定されていない var パラメータを受け取るために発生します。要件を満たしていないインターフェイス参照を渡しています。残念ながら、実行時にしかわかりません。これは、私の見解では、 の使用に対する最大のポイントですFreeAndNil

参照カウントは、インターフェイス参照カウント メカニズムによる有効期間管理を無効にします。オブジェクトを破棄するには、そのデストラクタを呼び出す必要があります。そのためには、デストラクタにアクセスできる必要があります。あなたのインターフェースはデストラクタを公開していません(公開すべきではありません)。したがって、オブジェクトを破棄するには、オブジェクト参照が必要であると推測できます。

以下にいくつかのオプションを示します。

var
  obj: TPersona;
  intf: IPersona;
....
obj := TPersona.Create('Fabio');
try
  intf := obj;
  //do stuff with intf
finally
  obj.Free;
  // or FreeAndNil(obj) if you prefer
end;

または、このようにすることもできます

var
  intf: IPersona;
....
intf := TPersona.Create('Fabio');
try
  //do stuff with intf
finally
  (intf as TObject).Free;
end;
于 2012-10-30T08:59:19.773 に答える
0

FreeAndNil()インターフェイス参照では使用できません。オブジェクト参照のみです。インターフェイスの参照カウントを有効のままにしていた場合は、単にnilインターフェイス参照に割り当てて (またはスコープから外して)、オブジェクトを正しく解放します。次に例を示します。

type
  TPersona = class(TInterfacedObject, IPersona)
  strict private
    FNome: string;
    function GetNome: string;
    procedure SetNome(const Value: string);
  public
    constructor Create(const ANome: string);
    destructor Destroy; override;
  end;

procedure TForm4.btn1Click(Sender: TObject);
var
  p: IPersona;
begin
  p := TPersona.Create('Fabio');
  try
    ShowMessage(p.Nome);
  finally
    p := nil;
  end;
end;

ただし、インターフェイスで参照カウントを無効にしたため、コードで通常のオブジェクト参照変数を使用するように戻す必要があります。たとえば、次のようになります。

procedure TForm4.btn1Click(Sender: TObject);
var
  p: TPersona;
  intf: IPersona;
begin
  p := TPersona.Create('Fabio');
  try
    if Supports(p, IPersona, intf) then
      ShowMessage(intf.Nome);
  finally
    FreeAndNil(p);
  end;
end;
于 2012-10-30T17:48:42.940 に答える