델파이에서 싱글턴 메모리 누수, Create 문제 없이 구현하기

델파이에서 싱글턴을 한다고 하면 보통 다음과 같은 소스를 많이 제시한다.

델파이에서 싱글턴 구현…

이런 소스의 문제는
1. 종료할 때 해제가 안 된다는 점
2. 여전히 Create가 가능하다는 점

이 있다.

첫째의 경우 싱글턴의 특성상 누수의 양이 많아서 문제가 되는 건 아니다. 어차피 인스턴스는 하나만 존재하기 때문이다.
그러나 ReportMemoryLeaksOnShutdown같은 옵션을 쓴다면 경고가 신경쓰이며 잘못하면 진짜 누수를 찾지 못하게 될 수도 있다.

둘째의 경우는 그 자체만으로도 치명적이다.
다른 언어 싱글턴 구현을 보고 구현하는 경우 private 생성자 Create를 만들게 마련인데, 델파이에선 그렇게 해도 public constructor Create는 그대로 남아있다.
public으로 선언된 TObject.Create는 private 생성자로는 가릴 수가 없기 때문이다!

결론적으로 소스는 다음과 같이 구현하면 위의 두 문제를 해결할 수 있다 (위의 원본 소스에서 수정하였다).
물론 Destroy를 직접 부르거나 FreeAndNil로 제거하는 등의 상황에서는 참사를 막지 못하겠지만, 적어도 일반적인 상황에서 나타날 수 있는 실수는 많은 부분 막을 수 있다.

unit Singleton;

interface

uses SysUtils;

type
    TSingleTon = class
    private
        procedure FreeSingletonInstance;
        procedure CreateSingletonInstance;
        class var Instance: TSingleTon;
    public
        class procedure Create;
        class procedure Free;
        class function GetInstance: TSingleTon;
    end;

implementation

class procedure TSingleTon.Create;
begin
    raise ENoConstructException.Create('It''s a singleton class!');
end;

class procedure TSingleTon.Free;
begin
    raise EInvalidOpException.Create('It''s a singleton class!');
end;

procedure TSingleTon.FreeSingletonInstance;
begin
    //Write destructor here
    inherited Free;
end;

procedure TSingleTon.CreateSingletonInstance;
begin
    //Write constructor here
end;

class function TSingleTon.GetInstance: TSingleTon;
begin
    if Instance = nil then
    begin
        result := inherited Create as self;
        result.CreateSingletonInstance;
    end
    else
    result := Instance;
end;

initialization
finalization
    TSingleTon.Instance.FreeSingletonInstance;
end.

원래 생성자와 파괴자 코드에 들어가야 할 코드는 //Write constructor, //Write destructor로 시작하는 부분에 각각 넣어주면 된다.
사실 FreeAndNil같은 것도 플래그 하나를 더 넣으면 막을 수 있겠지만 이 상태도 이미 행사 코드가 너무 길어서 넣지 않았다.
행사 코드가 상당히 길어서 마음에 들지는 않지만 이 정도는 되어야 타 언어 싱글턴 구현에 가까운 편의성을 지니게 되니, 매우 안타깝다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.