프로그래밍/델파이

Interface(TInterfacedObject)와 TInterfaceList 이야기

panpro 2006. 10. 10. 20:34

TInterfaceList에 Interface를 하나 추가하면 그 해당 Inteface item의 RefCount는 어떻게 될까?
굉장히 궁금했던 사항인데 오늘 그 결과를 속시원히 파헤쳐 보았다.


1. TInterfaceList에 Inteface를 하나 추가하면 해당 Interface Item의 RefCount가 늘어날까?
-> 증가한다. 1만큼 증가한다.

2. 그럼 TInterfaceList에서 item 하나를 빼면?
-> RefCount가 1만큼 감소한다. 감소만 하는거지 TInterfaceList에서 뺐다고 해서 자동으로 삭제되는 건 아니다. 그냥 1만큼 감소만 하는거다. 그런데 만약 여기서 빼서 RefCount = 0이 되면 그 때는 자동으로 삭제된다.

3. Interface를 하나 생성하면 생성할 당시의 RefCount는 얼마인가?
1이다. 생성되는 순간 1이 된다.

4. 인스턴스가 생성되었는지 여부를 알 수 있는 방법은 없는가?
있다. Interface가 하나 생성되면 procedure AfterConstruction; override; 이 함수가 호출되어 진다. 해제될 때는 procedure BeforeDestruction; override; 함수가 호출된다.

5. 만약 같은 TInterfaceList에 여러 번 Add 하면 RefCount는 어떻게 되는가?
추가한 만큼 RefCount가 올라간다. 만약 2번 Add했다면 RefCount는 2만큼 올라간다.

6. interface를 담고 있던 TInterfaceList를 free하면 그 안의 interface 객체는 어떻게 되는가?
추가한 수만큼 RefCount가 빠진다.

테스트한 전체 코드는 다음과 같다.
unit Unit1;

interface

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

type
  IFoo = Interface
  function GetA() : Integer;
end;

type
  TFoo = Class(TInterfacedObject, IFoo)
  function GetA() : Integer;
  procedure BeforeDestruction; override;
  procedure AfterConstruction; override;
end;

type
  TForm1 = class(TForm)
  Button1: TButton;
  procedure Button1Click(Sender: TObject);
  private
  { Private declarations }
  public
  { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TFoo }

procedure TFoo.AfterConstruction;
begin
  inherited;
  showMessage('인스턴스 생성 후');
end;


procedure TFoo.BeforeDestruction;
begin
  inherited;
  showMessage('삭제 되기 직전');
end;

function TFoo.GetA: Integer;
begin
  result := 1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pIFoo : TFoo;
  list : TInterfaceList;
  nRef : Integer;


begin
  list  := TInterfaceList.Create;
  try
  // 생성시
  pIFoo := TFoo.Create;
  nRef := TInterfacedObject(pIFoo).refCount;
  showMessage('생성하고 난 직후 : ' + intToStr(nRef)); 


  // 일부러 하나 올리기
  pIFoo._AddRef();
  nRef := TInterfacedObject(pIFoo).refCount;
  showMessage('일부러 하나 올린 후 : ' + intToStr(nRef));

  // 일부러 하나 올리기
  pIFoo._AddRef();
  nRef := TInterfacedObject(pIFoo).refCount;
  showMessage('일부러 하나 올린 후 : ' + intToStr(nRef));

  // 일부러 하나 내리기
  pIFoo._Release();
  nRef := TInterfacedObject(pIFoo).refCount;
  showMessage('일부러 하나 내린 후 : ' + intToStr(nRef));


  // 리스트에 추가한 후
  list.Add(pIFoo);
  nRef := TInterfacedObject(pIFoo).refCount;
  showMessage('리스트에 추가 후 ' + intToStr(nRef));

  // 리스트에서 삭제한 후
  list.Remove(pIFoo);
  nRef := TInterfacedObject(pIFoo).refCount;
  showMessage('리스트에서 삭제 후' + intToStr(nRef));


  // pIFoo가 자동으로 삭제되면 Dangling Pointer가 된다.
  // 그래서 Assigned() 함수로도 삭제가 된 인터페이스인지 여부를 확인할 수 없다.
  // 그리고 이미 자동으로 삭제된 인터페이스는 RefCount가 이미 쓰레기값이기 때문에
  // RefCount를 확인하는 것도 아무런 의미가 없다.
  if (pIFoo = NIL) then
     showMessage('pIFoo = NIL');

  if (NOT assigned(pIFoo)) then
     showMessage('pIFoo is NOT Assigned');

  finally
  list.Free;
  end;

  // TInterfaceList를 해제한 후.
  nRef := TInterfacedObject(pIFoo).refCount;
  showMessage('리스트를 해제한 후' + intToStr(nRef));
end;

end.