Become a member!

DelphiGuard - Automatic Memory Management for Delphi

What is DelphiGuard?

DelphiGuard is a free, open-source library that implements the RAII (Resource Acquisition Is Initialization) pattern for Delphi. It provides automatic memory management that eliminates memory leaks and reduces boilerplate code. Instead of nested try-finally blocks, you get clean, exception-safe code with automatic resource cleanup.

The Problem

Traditional Delphi memory management requires verbose try-finally blocks:

// Traditional approach - 20+ lines for 5 lines of logic
var
  Config: TIniFile;
  Connection: TFDConnection;
  Query: TFDQuery;
begin
  Config := TIniFile.Create('app.ini');
  try
    Connection := TFDConnection.Create(nil);
    try
      Query := TFDQuery.Create(nil);
      try
        // Actual business logic here
        Query.SQL.Text := Config.ReadString('SQL', 'Query', '');
        Query.Connection := Connection;
        Query.Open;
      finally
        Query.Free;
      end;
    finally
      Connection.Free;
    end;
  finally
    Config.Free;
  end;
end;

The Solution

DelphiGuard provides two patterns: Guard for single objects and ResourceManager for groups.

Guard Pattern (1-3 Objects)

uses DGuard;

// One line - automatic cleanup when scope exits
var lConfig := Using.Guard(TIniFile.Create('app.ini'));
Result := lConfig.Resource.ReadString('Settings', 'Key', '');
// lConfig automatically freed here

ResourceManager Pattern (4+ Objects)

uses DGuard;

var lResources := NewResourceScope();
var lConnection := lResources.Manager.Add(TFDConnection.Create(nil));
var lQuery := lResources.Manager.Add(TFDQuery.Create(nil));
var lDataset := lResources.Manager.Add(TFDMemTable.Create(nil));

// Use all resources...
lQuery.Connection := lConnection;
lQuery.Open;

// All resources freed automatically when lResources goes out of scope

When to Use Each Pattern

Use Guard When:

  • Managing 1-3 objects
  • Objects have independent lifecycles
  • Methods are simple and focused
  • Performance is critical (minimal overhead)
// HTTP request
var lClient := Using.Guard(THTTPClient.Create);
Result := lClient.Resource.Get(URL).ContentAsString;

// Database query
var lQuery := Using.Guard(TFDQuery.Create(nil));
lQuery.Resource.Connection := FConnection;
lQuery.Resource.SQL.Text := 'SELECT * FROM users';
lQuery.Resource.Open;

// File processing
var lStream := Using.Guard(TFileStream.Create(FileName, fmOpenRead));
lStream.Resource.Read(Buffer, SizeOf(Buffer));

Use ResourceManager When:

  • Managing 4+ related objects
  • Objects have dependent lifecycles
  • Complex scenarios with multiple failure points
  • Need logging/debugging of resource lifecycle
// Database transaction with validation
var lResources := NewResourceScope();
var lConn := lResources.Manager.Add(TFDConnection.Create(nil));
var lQuery := lResources.Manager.Add(TFDQuery.Create(nil));
var lValidator := lResources.Manager.Add(TDataValidator.Create);
var lLogger := lResources.Manager.Add(TFileLogger.Create('transaction.log'));

lConn.StartTransaction;
try
  // Business logic...
  lConn.Commit;
except
  lConn.Rollback;
  raise;
end;
// All 4 objects freed automatically

Debug Mode

Enable logging to track resource lifecycle:

// Debug scope with automatic logging
var lResources := NewDebugResourceScope();

// Or with custom callback
var lResources := NewResourceScope(
  procedure(const Msg: string)
  begin
    OutputDebugString(PChar(Msg));
  end
);

Real-World Examples

Email with Attachments

procedure SendReport;
var
  lResources: IResourceScope;
  lEmail: TIdMessage;
  lSMTP: TIdSMTP;
  lAttachment: TIdAttachmentFile;
begin
  lResources := NewResourceScope();
  lEmail := lResources.Manager.Add(TIdMessage.Create(nil));
  lSMTP := lResources.Manager.Add(TIdSMTP.Create(nil));
  lAttachment := lResources.Manager.Add(TIdAttachmentFile.Create(lEmail.MessageParts, 'report.pdf'));

  lEmail.Subject := 'Monthly Report';
  lEmail.Recipients.Add.Address := 'manager@example.com';

  lSMTP.Host := 'smtp.example.com';
  lSMTP.Connect;
  try
    lSMTP.Send(lEmail);
  finally
    lSMTP.Disconnect;
  end;
  // All resources cleaned up automatically
end;

Data Processing Pipeline

function ProcessCSV(const FileName: string): TDataset;
var
  lResources: IResourceScope;
  lReader: TStreamReader;
  lParser: TCSVParser;
  lValidator: TDataValidator;
begin
  lResources := NewResourceScope();
  lReader := lResources.Manager.Add(TStreamReader.Create(FileName));
  lParser := lResources.Manager.Add(TCSVParser.Create);
  lValidator := lResources.Manager.Add(TDataValidator.Create);

  while not lReader.EndOfStream do
  begin
    var Line := lReader.ReadLine;
    var Record := lParser.Parse(Line);
    if lValidator.Validate(Record) then
      Result.AppendRecord(Record);
  end;
end;

Key Benefits

Feature Description
Exception-Safe Resources freed even when exceptions occur
Clear Ownership No ambiguity about who owns what
Debug Support Optional lifecycle logging
Cross-Platform Win32, Win64, Linux, macOS, Android
Zero Dependencies Pure Delphi, no external libraries

When to Keep try-finally

DelphiGuard doesn’t replace all try-finally usage. Keep manual management when:

  • You need explicit destruction order control
  • Complex conditional object creation
  • Integration with legacy frameworks requiring specific patterns

Installation

  1. Download from GitHub
  2. Add the source folder to your Library Path
  3. Add uses DGuard; to your unit

FAQ: Frequently Asked Questions

How do I avoid memory leaks in Delphi?

Use DelphiGuard’s RAII pattern: var obj := Using.Guard(TMyClass.Create); - the object is automatically freed when it goes out of scope, even if exceptions occur.

What is RAII in Delphi?

RAII (Resource Acquisition Is Initialization) ties resource lifetime to object scope. DelphiGuard implements this pattern using interfaces, so when the interface reference goes out of scope, resources are automatically freed.

How do I automatically free objects in Delphi?

Use DelphiGuard’s Guard pattern:

var lStream := Using.Guard(TFileStream.Create('file.txt', fmOpenRead));
// Use lStream.Resource...
// Automatically freed when scope exits

Is there a smart pointer for Delphi?

DelphiGuard provides smart pointer-like functionality through the Guard pattern. It wraps objects and automatically manages their lifetime.

How do I reduce try-finally boilerplate in Delphi?

DelphiGuard eliminates nested try-finally blocks. Instead of 20+ lines of cleanup code, you write clean one-liners that handle cleanup automatically.

Is DelphiGuard exception-safe?

Yes, DelphiGuard guarantees resource cleanup even when exceptions occur. The interface reference mechanism ensures destructors are called during stack unwinding.

Can I manage multiple objects with DelphiGuard?

Yes, use ResourceManager for 4+ related objects:

var lResources := NewResourceScope();
var obj1 := lResources.Manager.Add(TClass1.Create);
var obj2 := lResources.Manager.Add(TClass2.Create);
// All freed automatically

Does DelphiGuard work cross-platform?

Yes, DelphiGuard supports Win32, Win64, Linux, macOS, and Android.


DelphiGuard - RAII for Delphi. Automatic memory management. Exception-safe. Open-source.

Comments

comments powered by Disqus