본문 바로가기
Delphi/클래스

델파이 TDictionary / TObjectDictionary

by MonoSoft 2021. 8. 4.
728x90
반응형

델파이 TDictionary TObjectDictionary 

사용 완료후 아이템 메모리 해제

 

 

델파이 2009에 새로 추가된 TDictionary 클래스 입니다.

 

우선 클래스는 Generics.Collections 에 들어 있습니다.

 

소스를 보시면

TDictionary<TKey,TValue>

이런 형태로 정의가 되어 있습니다.

사용법은 hash table과 

거의 흡사하다고 보시면 됩니다.

 

key값과 value가 변경될때 이벤트도 발생

시켜 주기 때문에 이벤트를 연결해서

사용할 수도 있습니다.

 

hash table과 검색 속도를 비교해 봤는데

데이터가 많아질수록 이 클래스가 빨랐습니다.

뭐 hash table도 데이터가 많아질때

좋은 속도를 낸다는 점에서는 비슷하기는 하지만

 

어쨌든 제가 테스트한 hash table과

Dictionary에서는  Dictionary가 빨랐습니다.

 

그럼 아주 심플한 사용 예를 보여 드리겠습니다.

 

예를 들어 클라이언트를 관리 한다고 하면

 

var

  Dic : TDictionary<string, TMyClient>;

begin

  Dic := TDictionary<string, TMyClient>.Create;

  try  

    Dic.Add('cli001', Myclient);

    // 어쩌구 저쩌구~~~ //

  finally

    Dic.Free;

  end;

end;

 

이런식으로 사용하고 key값으로 검색을 하시면 됩니다.

 

아래 샘플을 보시면 Memory Leak이 발생 한다고 하는데

마지막 Free 전에 Values 와

KeyCollection을 Free 시켜주면 된답니다.

 

procedure TForm4.Button2Click(Sender: TObject);

var

  Dic : TDictionary<Integer,TPerson>;

  p : TPerson;

  i : integer;

begin

  //Create dictionary

  Dic := TDictionary<Integer,TPerson>.Create;

  Dic.Add(1, TPerson.Create('Delphi', 'Mr'));

  Dic.Add(2, TPerson.Create('Generic', 'Bill'));

  Dic.Add(3, TPerson.Create('nonymous', 'An'));

  try

  //Travel the strings

  for p in Dic.Values  do begin

    ShowMessage(p.FullName);

  end;

 

  //Travel the keys

  for i in Dic.Keys do begin

    ShowMessage(IntToStr(i) + ': ' +

                       Dic.Items[i].FullName);

  end;

 

  //Find some key

  if Dic.TryGetValue(3, p) then begin

    ShowMessage('Found it!: ' + p.FullName);

  end;

  finally

    for p in Dic.Values do

      p.Free;

    //Also free Values and KeyCollection

    //other wise you have a memoryleak

    //Is this a bug?

    Dic.Values.Free;

    Dic.Keys.Free;

    //Free the dictionary

    Dic.Free;

  end;

 

샘플에 대한 자세한 내용은 아래 URL을 참고 하시기 바랍니다.


http://beensoft.blogspot.com/2008/09/simple-generic-dictionary-tdictionary.html%EF%BB%BF

제너릭을 지원하고 쉬운 사용이 가능하기 때문에 

유용한 클래스 이지만 메모리 관리가 불편하다는

단점이 있네요. 상속을 받아 메모리 누수를

해결 해서 사용하면 아주 편리하지 않을까 생각합니다.

 

C#에서 사용되는 것은 봤는데

델파이2009에서도 추가가 되어 있어 참 반가웠습니다.

 

hash table을 써도 무관하겠지만 제너릭을 

지원한다는데에 매력이 있는 것 같습니다

 

Delphi 2009 에서 Generic이 추가되면서

유용한 클래스들이 추가되었다.

특히나 특정 Key와 쌍을 이루는 값을 저장할때

TDictionary는 참 유용하게 쓰인다.

일반 데이터 형을 보관할 때는 상관이 없으나 

Class를 보관할때는 메모리 해제에 주의해야 한다.

 

uses
  Generics.Collections;
type
  TTempKey = class
  end;
  TTempValue = class  end;

 

1. TDictionary

 

 

procedure TForm2.Button1Click(Sender: TObject);
var
  Dic: TDictionary<Integer,TTempValue>;
  Key: Integer;
begin
  Dic := TDictionary<Integer,TTempValue>.Create;
  try
    Dic.Add( 1, TTempValue.Create );

    for Key in Dic.Keys do
      Dic.Items[Key].Free;
  finally
    Dic.Free;
  end;
end;

Key와 매칭되는 Value를 저장할 수 있다.

여기서는 Key의 형을 Integer, Value의 형은

TTempValue 클래스로 설정했다.

Dic만 해제하면 TDictonary의 Values에

저장된 TTempValue 인스턴스가 붕 떠서 Memory Leak이 발생한다.

 

반드시 Dic를 해제하기 전에 Dic의 아이템들을 해제해야 한다.

 

2. TObjectDictionary

 

예전부터 Contnrs 유닛에 있던 TObjectList와 비슷하다.

생성시에 OwnsObject를 True로 설정해서

자동으로 아이템의 메모리를 해제했던 기억이 있을 것이다.

 

(1) Key와 Value 가 모두 Class형인 경우

 

procedure TForm2.Button2Click(Sender: TObject);
var
  Dic: TObjectDictionary<TTempKey,TTempValue>;
begin
  Dic := TObjectDictionary<TTempKey,TTempValue>.Create( [doOwnsKeys, doOwnsValues] );
  try
    Dic.Add( TTempKey.Create, TTempValue.Create );
  finally
    Dic.Free;
  end;
end;

 

인스턴스 생성시에 생성자의 인자를 보면 

TDictionaryOwnerships라는 집합형을 넘긴다.

TObjectList의 OwnsObject를 생각하면 된다.

Key와 Value가 모두 클래스 형이므로

둘다 소유하는 것으로 설정했다.

 

 

(2) Value만 Class형인 경우

 

procedure TForm2.Button3Click(Sender: TObject);
var
  Dic: TObjectDictionary<Integer,TTempValue>;
begin
  Dic := TObjectDictionary<Integer,TTempValue>.Create( [doOwnsValues] );

  try
    Dic.Add( 1, TTempValue.Create );
  finally
    Dic.Free;
  end;
end;

 

Value만 클래스 형인 경우 Key를 소유하라고 

doOwnsKeys를 설정하면 익셉션이 발생한다.

 

익셉션 클래스는 EInvalidCast, 메시지는 Invalid class typecast.

 

 

unit Unit3;

interface

 

uses

Winapi.Windows, Winapi.Messages, System.SysUtils,

System.Variants, System.Classes, Vcl.Graphics,

Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Generics.Collections, Vcl.StdCtrls;

type

TForm3 = class(TForm)

Memo1: TMemo;

Button1: TButton;

Button2: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

 

type

TPerson = class(Tobject)

public

ID : integer;

OtherRef : String;

Name : string;

constructor Create(ID : integer; OtherREf, Name : STring);

function Equals(Obj : TObject): Boolean; override;

function GetHashCode : Integer; override;

end;

 

var

Form3: TForm3;

 

implementation

 

{$R *.dfm}

 

{ TPerson }

 

constructor TPerson.Create(ID: integer; OtherREf, Name: STring);

begin

Self.ID := ID;

Self.OtherRef := OtherREf;

Self.Name := Name;

end;

 

procedure TForm3.Button1Click(Sender: TObject);

var

D : TObjectDictionary<TPerson,String>;

P : TPerson;

begin

D := TObjectDictionary<TPerson,string>.Create([doOwnsKeys]);

try

D.Add(TPerson.Create(1,'B97', 'Bob'), 'Info about Bob');

D.Add(TPerson.Create(2,'A32', 'Alice'), 'Info about Alice');

D.Add(TPerson.Create(3,'C16', 'Charles'), 'Info about Charles');

 

P := TPerson.Create(1,'B12', 'Bill');

Memo1.Lines.Add(D[P]);

P.Free;

finally

D.Free;

end;

end;

 

function TPerson.Equals(Obj: TObject): Boolean;

begin

if Obj = nil then

Result := False

else

Result := (Obj is TPerson) and ((Obj as TPerson).ID = ID) and ((Obj as TPerson).OtherRef = OtherRef);

end;

 

function TPerson.GetHashCode: Integer;

begin

Result := OtherREf.GetHashCode xor ID;

end;

 

procedure TForm3.Button2Click(Sender: TObject);

var

iDictionary: TDictionary<string, string>;

begin

iDictionary := TDictionary<string, string>.Create;

try

iDictionary.Add('Monday','TEll mey I don ''t like these');

iDictionary.Add('Tuesday','Is full of grace');

iDictionary.Add('Wednesday','Hump Day!!!');
Memo1.Lines.Add(iDictionary['Monday'])

finally

iDictionary.Free;

end;

end;

end.

728x90
반응형

댓글