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
- Download from GitHub
- Add the source folder to your Library Path
- Add
uses DGuard;to your unit
Links
- GitHub: github.com/danieleteti/delphiguard
- Support: Facebook Group
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