Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Design Pattern in Delphi: Singleton

memory leak design pattern

  • Please log in to reply
No replies to this topic

#1 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1320 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 20 November 2011 - 09:23 PM

Quoted from wikipedia (Singleton pattern):

In software engineering, the singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects.


Basically, there can be only one instance of a singleton class throughout the system. In Delphi there are three common methods (that I aware of) to implement singleton class.
  • Using global variable
  • Using global function
  • Internally managed by the class

Personally I call the last one as the "true" singleton implementation. You will see the reason later when I explain the method.

1. Using global variable

In this method, the singleton object is referenced by a global variable. Usually the object is instantiated in initialization section of the unit and later be freed in finalization unit. Something like this:
unit GlobalVarSingleton;

interface

type
  TConfig=class
  private
    FName: string;
    FTimeStamp: TDateTime;
  public
    property Name: string read FName write FName;
    property TimeStamp: TDateTime read FTimeStamp write FTimeStamp;
  end;

var
  [b]Config: TConfig;[/b]  // this is our global variable of our singleton

implementation

initialization
  Config := TConfig.Create;

finalization
  // this line will raise EAccessViolation if the instance ever got
  // freed without setting Config variable to nil.
  Config.Free;
end.

This is the simplest method to achieve singleton class. However this method actually is still far from actually be singleton class. Since the caller has full control of constructing and destructing the corresponding object. Caller can simply choose to free existing one and create another one, which making information already accumulated in the old one lost.

Pros:
  • The most efficient, with the smallest overhead. Since we work with direct reference, no cpu cycles need to be wasted on stack operation.
  • The simplest to use.

Cons:
  • The most fragile of all methods. a bit confusion on your side (or any other coders) could easily lead to incorrect free-ing of the singleton object. Which lead to several errors which seemingly unrelated at first sight.

Note that although this method is the most efficient one, but in modern computers the overhead caused by other methods usually is insignificant.

2. Using global function

This technique is also called lazy loading, because the object will not be intantiated until the first call to the function. This method uses local unit variable instead of a global one. The global function checks if the singleton object is already created or not. When not, it creates the object and returns it. Something like this:

unit GlobalFunctionSingleton;

interface

type
  TConfig=class
  private
    FName: string;
    FTimeStamp: TDateTime;
  public
    property Name: string read FName write FName;
    property TimeStamp: TDateTime read FTimeStamp write FTimeStamp;
  end;

[b]function Config: TConfig;[/b] // note that Config now is a function

implementation

var
  uConfig: TConfig;

function Config: TConfig;
begin
  if uConfig=nil then
    uConfig := TConfig.Create;
  Result := uConfig;
end;

initialization

finalization
  // this line will raise EAccessViolation if the instance ever got
  // (incorrectly) freed through the instance returned by Config function
  uConfig.Free;
  
end.

Note that Config in the first unit (GlobalVarSingleton) is a global variable, but in the above Config is a global function;

Pros:
  • You can not accidentally create new instance of your singleton.
  • Better for "dead code removal". Since we are lazy loading instead of instatiating in initialization section, if in our application we never call Config or use any part of TConfig, then codes related with them will not be compiled into the final executable. Different if we always instatiated in initialization, where some codes related with TConfig will always be included in the final executable, even if we never actually use it on other part of our program.

Cons:
  • You still can (albeit usually it's accidentally) free the singleton. This will lead to access violation for subsequent calls to Config, and another one when the code enters finalization section.

3. Internally managed by the class
In this method, the singleton restrictions is done internally by the class. Not by some external code. The process is similar with global function technique, by checking a local unit variable.

True Requirements of Singleton
The other methods might be able to provide only one instance throughout the system's lifetime. But a little bit carelessness will yield into access violation error. Since singleton objects usually applied to important or key objects, making a little error on these will make the whole system unusable. A restart is inevitable.

Let's sum up the requirements of "true" singleton.
  • Except for the first time, creating new instance of "true" singleton should always returns existing one.
  • Destroying the singleton object will not really destroy it, unless done as designed (e.g. when application is terminated, or when another key/container object is destroyed).

And our true singleton goes something like this:

unit TrueSingleton;

interface

uses
  SysUtils
  ;

type
  TConfig=class
  private
    FName: string;
    FTimeStamp: TDateTime;
  public
    class function NewInstance: TObject; override;
    procedure FreeInstance; override;

    property Name: string read FName write FName;
    property TimeStamp: TDateTime read FTimeStamp write FTimeStamp;
  end;
  
implementation

var
  uConfig: TObject;
  uFinalized: Boolean;

{ TConfig }

procedure TConfig.FreeInstance;
begin
  if uFinalized then
    inherited FreeInstance;
end;

class function TConfig.NewInstance: TObject;
begin
  if uConfig=nil then
    uConfig := inherited NewInstance;

  Result := uConfig;
end;

initialization

finalization
  uFinalized := True;
  uConfig.Free;
end.

Class Function NewInstance
When a class is to be instantiated, the class asks memory manager to provide memory location large enough for new instance of the class. This is done by virtual class function NewInstance. We can override this method if we want to reuse a special memory location.

In our sample code, we want any construction call of TConfig to always result in instance pointed by uConfig. Of course if uConfig has not been initiated (indicated by its value of nil), we want to call "original" NewInstance which will handle the process with memory manager.

Procedure FreeInstance
When an object is destroyed, it will call virtual procedure FreeInstance. This method is responsible to "return" the memory previously occupied by the object to memory manager. So this is where the object got wiped out.

If we want to prevent our singleton to be wiped out before designated time (i.e. before our program is terminated), this method is the best place. So, let's override this method. In the overriding method we checks if the freeing is done in finalization section or not. When the freeing is done in finalization section (seen from the uFinalized flag) we continue to the "original" FreeInstance otherwise we ignore the freeing request.

Pros:
  • More reliable. There is no chance that you accidentally free the singleton.
  • "Dead code removal"-friendly.
  • Less possible memory leak, since you can keep using this pattern of code that is very good practice in preventing memory leak:
      vObject := TMyClass.Create;
      try
        ...
    	...
    	...
      finally
        vObject.Free;
      end;
    
  • This method might help promoting low coupling principle in your projects. Especially if you use same base framework for many of your projects.

Cons:
  • Higher overhead compared to other methods. However this is very insignificant compared to the power of nowadays computers.
  • You can not do local variables initialization and finalization in by usual overriding of Create and Destroy methods. Instead you must do initializations in NewInstance and finalizations in FreeInstance.

Demo Project
The attached demo project explores the pros and cons of each singleton method explained above. For each method, the demo project will do the followings:
  • Change a property of our singleton object
  • Inspect a property, in conjuction with the above this shows that the change was really done on the same instance.
  • Free the singleton and try to display its property afterward.
  • Construct two instances from the singleton class and see if they are actually the same instance.

And here are some screenshots of the demo program.

  • Using global variable
    GlobalVarSingleton_PopName_00.png

    GlobalVarSingleton_TryDestroyThenPopName_00.png

    GlobalVarSingleton_Compare_00.png
  • Using global function
    GlobalFuncSingleton_PopName_00.png

    GlobalFuncSingleton_TryDestroyThenPopName_00.png

    GlobalFuncSingleton_Compare_00.png

  • "True" singleton
    TrueSingleton_PopName_00.png

    TrueSingleton_TryDestroyThenPopName_00.png

    TrueSingleton_Compare_00.png

The source code of the demo project and sample of implementations is here (Attached File  SingletonClass_Demo.zip   4.05KB   403 downloads). Feel free to use and improve it to your liking.

Edited by LuthfiHakim, 05 December 2011 - 10:31 PM.

  • 0





Also tagged with one or more of these keywords: memory leak, design pattern

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download