開発ブログ - TInterfacedObject ではまる

TInterfacedObject ではまる

カテゴリ : 
技術メモ » Delphi » 全般
執筆 : 
2014/1/9

知ってはいたんですよ。
TInterfacedObject の自動破棄については…
いえ、知っているつもりだったんです。

TInterfacedObject を継承して作ったクラスを使うときには Free をしちゃだめ!
という知識はありました。
ただ、それは局所変数で処理しているから、局所変数がなくなったときに自動破棄されるものだと思っていたのです。

知っていたサンプル

たとえば下に示すようなクラスを作ったとします。
単に Test というプロシジャだけを持つ interface と、それを実装した TInterfacedObject を継承したクラスです。


interface

uses
  System.Classes;

type
  ITestInterface = interface
    ['{7723B047-28A4-4EF1-8836-0BE06092F2F5}']
    procedure Test;
  end;

  TTestClass = class(TInterfacedObject, ITestInterface)
  public
    constructor Create;
    destructor Destroy; override;
    procedure Test;
  end;

implementation

uses
  Vcl.Dialogs;

{ TTestClass }

constructor TTestClass.Create;
begin
  inherited;
  ShowMessage('Create!');
end;

destructor TTestClass.Destroy;
begin
  ShowMessage('Destroy!!');
  inherited;
end;

procedure TTestClass.Test;
begin
  ShowMessage('Test');
end;

end.

問題が起きるのは次のような書き方をした場合だと思っていました。


procedure Test;
var
  T: TTestClass;
begin
  T := TTestClass.Create;
  (T as ITestInterface).Test;
  T.Free;
end;

大丈夫だと思っていたサンプル

フォームからこのクラスを使おうとして、フォーム生成時にクラスを作成し、ボタンが押されたら Test メソッドを呼び出す、というコードを書いてみました。


type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    FTest: TTestClass;
  public
    { Public 宣言 }
  end;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  FTest := TTestClass.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FTest);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  (FTest as ITestInterface).Test;
end;

このコードを実行すると、2回目にボタンを押すと例外が発生してしまいます。
(FTest as ITestInterface).Test; を実行すると、Button1Click を抜けるタイミングで FTest の実体が解放されてしまうためです。
えー!そんなぁ!!!
このサンプルくらいなら FTest を ITestInterface で宣言するという手が使えますが、複数の Interface を持つクラスを保持するためにその数分の変数を用意するのはないなぁ…と思うのです。
やはり TInterfacedObject を Java の Interface 的に使うのは、どんなときでもだめ!ということですね。

今回、かなり複雑な仕組みのプロジェクトに、TInterfaceObject と Interface の機構を後から組み込んでしまい、わけのわからないバグに悩まされてしまいました。
もう絶対 TInterfacedObject は使わないことを心に誓いましたよ。

解決策は?

参考サイトの情報を参照してください〜

[参考サイト]

Delphi/interfaceを使おう/何が問題になるか?
TInterfacedObject で自動破棄....の前に

  • コメント (0)
  • トラックバック (0)

コメントの投稿

お気軽にコメントください : 投稿されたコメントは承認後に表示されます
プロダクツ

開発ブログ

Link

AD